- Приспособленец (шаблон проектирования)
-
Шаблон проектирования Приспособленец Flyweight Тип: структурный
Описан в Design Patterns Да
Приспособленец (англ. Flyweight) — это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым.
Содержание
Цель
Оптимизация работы с памятью, путем предотвращения создания экземпляров элементов, имеющих общую сущность.
Описание
Flyweight используется для уменьшения затрат при работе с большим количеством мелких объектов. При проектировании приспособленца необходимо разделить его свойства на внешние и внутренние. Внутренние свойства всегда неизменны, тогда как внешние могут отличаться в зависимости от места и контекста применения и должны быть вынесены за пределы приспособленца.
Flyweight дополняет паттерн Factory таким образом, что Factory при обращении к ней клиента для создания нового объекта ищет уже созданный объект с такими же параметрами, что и у требуемого, и возвращает его клиенту. Если такого объекта нет, то фабрика создаст новый.
Примеры
Пример #1 на Java
Исходный текст на языке Javaimport 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. }
Пример #2 на Java
Исходный текст на языке Javapackage com.designpatterns.flyweight; /* Файл EnglishCharacter.java * * */ public abstract class EnglishCharacter { protected char simbol; protected int width; protected int height; public abstract void printCharacter(); } package com.designpatterns.flyweight; /* Файл CharacterA.java * * */ public class CharacterA extends EnglishCharacter { public CharacterA(){ simbol = 'A'; width = 10; height = 20; } @Override public void printCharacter() { System.out.println("Simbol = " + simbol + " Width = " + width + " Height = " + height); } } package com.designpatterns.flyweight; /* Файл CharacterB.java * * */ public class CharacterB extends EnglishCharacter { public CharacterB(){ simbol = 'B'; width = 20; height = 30; } @Override public void printCharacter() { System.out.println("Simbol = " + simbol + " Width = " + width + " Height = " + height); } } package com.designpatterns.flyweight; /* Файл CharacterC.java * * */ public class CharacterC extends EnglishCharacter { public CharacterC(){ simbol = 'C'; width = 40; height = 50; } @Override public void printCharacter() { System.out.println("Simbol = " + simbol + " Width = " + width + " Height = " + height); } } package com.designpatterns.flyweight; /* Файл FlyweightFactory.java * * */ import java.util.HashMap; public class FlyweightFactory { private HashMap<Integer, EnglishCharacter> characters = new HashMap<Integer, EnglishCharacter>(); public EnglishCharacter getCharacter(int characterCode){ EnglishCharacter character = characters.get(new Integer(characterCode)); if (null == character){ switch (characterCode){ case 1 : { character = new CharacterA(); break; } case 2 : { character = new CharacterB(); break; } case 3 : { character = new CharacterC(); break; } } characters.put(characterCode, character); } return character; } } package com.designpatterns.flyweight; /* Класс, показывающий работу шаблона проектирования "Приспособленец". * Файл Application.java * * */ public class Application { public static void main (String [] args){ FlyweightFactory factory = new FlyweightFactory(); int [] characterCodes = {1,2,3}; for (int nextCode : characterCodes){ EnglishCharacter character = factory.getCharacter(nextCode); character.printCharacter(); } } }
Пример на C#
Исходный текст на языке C#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 + ")"); } } }
Пример на C++
Исходный текст на языке C++#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; }
Пример на PHP5
Исходный текст на языке PHP<?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); } ?>
Пример на VB.NET
Исходный текст на VB.NETImports 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
Символы в Smalltalk практически идентичны «обычным строкам», но не порождаются каждый раз заново. Два идентичных символа на самом деле всегда являются одним и тем же экземпляром класса Symbol, тогда как две идентичные строки могут быть разными экземплярами класса String.
Ссылки
- Паттерн Flyweight (приспособленец) — назначение, описание, особенности и реализация на С++.
Шаблоны проектирования Основные Порождающие Структурные Адаптер • Выделение частного класса данных • Декоратор • Заместитель • Компоновщик • Мост • Приспособленец • Фасад
Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Категории:- Шаблоны проектирования
- Структурные шаблоны проектирования
Wikimedia Foundation. 2010.