22-10-2023
Приспособленец | |
Flyweight | |
Тип: |
структурный |
---|---|
Описан в Design Patterns |
Да |
Приспособленец (англ. Flyweight) — это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым.
Содержание |
Оптимизация работы с памятью, путем предотвращения создания экземпляров элементов, имеющих общую сущность.
Flyweight используется для уменьшения затрат при работе с большим количеством мелких объектов. При проектировании приспособленца необходимо разделить его свойства на внешние и внутренние. Внутренние свойства всегда неизменны, тогда как внешние могут отличаться в зависимости от места и контекста применения и должны быть вынесены за пределы приспособленца.
Flyweight дополняет паттерн Factory таким образом, что Factory при обращении к ней клиента для создания нового объекта ищет уже созданный объект с такими же параметрами, что и у требуемого, и возвращает его клиенту. Если такого объекта нет, то фабрика создаст новый.
import java.util.*; public enum FontEffect { BOLD, ITALIC, SUPERSCRIPT, SUBSCRIPT, STRIKETHROUGH } public final class FontData { /** * A weak hash map will drop unused references to FontData. * Values have to be wrapped in WeakReferences, * because value objects in weak hash map are held by strong references. */ private static final WeakHashMap<FontData, WeakReference<FontData>> flyweightData = new WeakHashMap<FontData, WeakReference<FontData>>(); private final int pointSize; private final String fontFace; private final Color color; private final Set<FontEffect> effects; private FontData(int pointSize, String fontFace, Color color, EnumSet<FontEffect> effects) { this.pointSize = pointSize; this.fontFace = fontFace; this.color = color; this.effects = Collections.unmodifiableSet(effects); } public static FontData create(int pointSize, String fontFace, Color color, FontEffect... effects) { EnumSet<FontEffect> effectsSet = EnumSet.noneOf(FontEffect.class); effectsSet.addAll(Arrays.asList(effects)); // We are unconcerned with object creation cost, we are reducing overall memory consumption FontData data = new FontData(pointSize, fontFace, color, effectsSet); if (!flyweightData.containsKey(data)) { flyweightData.put(data, new WeakReference<FontData> (data)); } // return the single immutable copy with the given values return flyweightData.get(data).get(); } @Override public boolean equals(Object obj) { if (obj instanceof FontData) { if (obj == this) { return true; } FontData other = (FontData) obj; return other.pointSize == pointSize && other.fontFace.equals(fontFace) && other.color.equals(color) && other.effects.equals(effects); } return false; } @Override public int hashCode() { return (pointSize * 37 + effects.hashCode() * 13) * fontFace.hashCode(); } // Getters for the font data, but no setters. FontData is immutable. }
using System; using System.Collections; namespace Flyweight { class MainApp { static void Main() { // Build a document with text string document = "AAZZBBZB"; char[] chars = document.ToCharArray(); CharacterFactory f = new CharacterFactory(); // extrinsic state int pointSize = 10; // For each character use a flyweight object foreach (char c in chars) { pointSize++; Character character = f.GetCharacter(c); character.Display(pointSize); } // Wait for user Console.Read(); } } // "FlyweightFactory" class CharacterFactory { private Hashtable characters = new Hashtable(); public Character GetCharacter(char key) { // Uses "lazy initialization" Character character = characters[key] as Character; if (character == null) { switch (key) { case 'A': character = new CharacterA(); break; case 'B': character = new CharacterB(); break; //... case 'Z': character = new CharacterZ(); break; } characters.Add(key, character); } return character; } } // "Flyweight" abstract class Character { protected char symbol; protected int width; protected int height; protected int ascent; protected int descent; protected int pointSize; public abstract void Display(int pointSize); } // "ConcreteFlyweight" class CharacterA : Character { // Constructor public CharacterA() { this.symbol = 'A'; this.height = 100; this.width = 120; this.ascent = 70; this.descent = 0; } public override void Display(int pointSize) { this.pointSize = pointSize; Console.WriteLine(this.symbol + " (pointsize " + this.pointSize + ")"); } } // "ConcreteFlyweight" class CharacterB : Character { // Constructor public CharacterB() { this.symbol = 'B'; this.height = 100; this.width = 140; this.ascent = 72; this.descent = 0; } public override void Display(int pointSize) { this.pointSize = pointSize; Console.WriteLine(this.symbol + " (pointsize " + this.pointSize + ")"); } } // ... C, D, E, etc. // "ConcreteFlyweight" class CharacterZ : Character { // Constructor public CharacterZ() { this.symbol = 'Z'; this.height = 100; this.width = 100; this.ascent = 68; this.descent = 0; } public override void Display(int pointSize) { this.pointSize = pointSize; Console.WriteLine(this.symbol + " (pointsize " + this.pointSize + ")"); } } }
#include <map> #include <iostream> // "Flyweight" class Character{ public: virtual void display() const = 0; protected: char mSymbol; int mWidth; int mHeight; int mAscent; int mDescent; int mPointSize; }; // "ConcreteFlyweight" class ConcreteCharacter : public Character{ public: // Constructor ConcreteCharacter( char aSymbol, int aPointSize ){ mSymbol = aSymbol; mHeight = 100; mWidth = 120; mAscent = 70; mDescent = 0; mPointSize = aPointSize; } // from Character virtual void display() const{ std::cout << mSymbol << " ( PointSize " << mPointSize << " )\n"; } }; // "FlyweightFactory" template< int POINT_SIZE = 10 > class CharacterFactory{ public: const Character& getCharacter( char aKey ){ // Uses "lazy initialization" Characters::const_iterator it = mCharacters.find( aKey ); if ( mCharacters.end() == it ){ const Character* character = new ConcreteCharacter(aKey, POINT_SIZE); mCharacters[aKey] = character; return *character; } else{ return *it->second; } } virtual ~CharacterFactory(){ for (Characters::const_iterator it = mCharacters.begin(); it != mCharacters.end(); it++) delete it->second; } private: typedef std::map< char, const Character* > Characters; Characters mCharacters; }; int main(){ std::string document = "AAZZBBZB"; CharacterFactory<12> characterFactory; for (std::string::const_iterator it = document.begin(); it != document.end(); it++){ const Character& character = characterFactory.getCharacter( *it ); character.display(); } return 0; }
<?php // "FlyweightFactory" class CharacterFactory { private $characters = array(); public function GetCharacter($key) { // Uses "lazy initialization" if (!array_key_exists($key, $this->characters)) { switch ($key) { case 'A': $this->characters[$key] = new CharacterA(); break; case 'B': $this->characters[$key] = new CharacterB(); break; //... case 'Z': $this->characters[$key] = new CharacterZ(); break; } } return $this->characters[$key]; } } // "Flyweight" abstract class Character { protected $symbol; protected $width; protected $height; protected $ascent; protected $descent; protected $pointSize; public abstract function Display($pointSize); } // "ConcreteFlyweight" class CharacterA extends Character { // Constructor public function __construct() { $this->symbol = 'A'; $this->height = 100; $this->width = 120; $this->ascent = 70; $this->descent = 0; } public function Display($pointSize) { $this->pointSize = $pointSize; print ($this->symbol." (pointsize ".$this->pointSize.")"); } } // "ConcreteFlyweight" class CharacterB extends Character { // Constructor public function __construct() { $this->symbol = 'B'; $this->height = 100; $this->width = 140; $this->ascent = 72; $this->descent = 0; } public function Display($pointSize) { $this->pointSize = $pointSize; print($this->symbol." (pointsize ".$this->pointSize.")"); } } // ... C, D, E, etc. // "ConcreteFlyweight" class CharacterZ extends Character { // Constructor public function __construct() { $this->symbol = 'Z'; $this->height = 100; $this->width = 100; $this->ascent = 68; $this->descent = 0; } public function Display($pointSize) { $this->pointSize = $pointSize; print($this->symbol." (pointsize ".$this->pointSize.")"); } } $document="AAZZBBZB"; // Build a document with text $chars=str_split($document); print_r($chars); $f = new CharacterFactory(); // extrinsic state $pointSize = 0; // For each character use a flyweight object foreach ($chars as $key) { $pointSize++; $character = $f->GetCharacter($key); $character->Display($pointSize); } ?>
Imports System.Collections Namespace Flyweight Class Program Shared Sub Main() ' Build a document with text Dim document As String = "AAZZBBZB" Dim chars As Char() = document.ToCharArray() Dim f As New CharacterFactory() ' extrinsic state Dim pointSize As Integer = 10 ' For each character use a flyweight object For Each c As Char In chars pointSize += 1 Dim character As Character = f.GetCharacter(c) character.Display(pointSize) Next ' Wait for user Console.Read() End Sub End Class ' "FlyweightFactory" Class CharacterFactory Private characters As New Hashtable() Public Function GetCharacter(ByVal key As Char) As Character ' Uses "lazy initialization" Dim character As Character = TryCast(characters(key), Character) If character Is Nothing Then Select Case key Case "A"c character = New CharacterA() Exit Select Case "B"c character = New CharacterB() Exit Select '... Case "Z"c character = New CharacterZ() Exit Select End Select characters.Add(key, character) End If Return character End Function End Class ' "Flyweight" MustInherit Class Character Protected symbol As Char Protected width As Integer Protected height As Integer Protected ascent As Integer Protected descent As Integer Protected pointSize As Integer Public MustOverride Sub Display(ByVal pointSize As Integer) End Class ' "ConcreteFlyweight" Class CharacterA Inherits Character ' Constructor Public Sub New() Me.symbol = "A"c Me.height = 100 Me.width = 120 Me.ascent = 70 Me.descent = 0 End Sub Public Overrides Sub Display(ByVal pointSize As Integer) Me.pointSize = pointSize Console.WriteLine(Me.symbol & " (pointsize " & Me.pointSize & ")") End Sub End Class ' "ConcreteFlyweight" Class CharacterB Inherits Character ' Constructor Public Sub New() Me.symbol = "B"c Me.height = 100 Me.width = 140 Me.ascent = 72 Me.descent = 0 End Sub Public Overrides Sub Display(ByVal pointSize As Integer) Me.pointSize = pointSize Console.WriteLine(Me.symbol & " (pointsize " & Me.pointSize & ")") End Sub End Class ' ... C, D, E, etc. ' "ConcreteFlyweight" Class CharacterZ Inherits Character ' Constructor Public Sub New() Me.symbol = "Z"c Me.height = 100 Me.width = 100 Me.ascent = 68 Me.descent = 0 End Sub Public Overrides Sub Display(ByVal pointSize As Integer) Me.pointSize = pointSize Console.WriteLine(Me.symbol & " (pointsize " & Me.pointSize & ")") End Sub End Class End Namespace
Символы в Smalltalk практически идентичны «обычным строкам», но не порождаются каждый раз заново. Два идентичных символа на самом деле всегда являются одним и тем же экземпляром класса Symbol, тогда как две идентичные строки могут быть разными экземплярами класса String.
Flyweight.