Приспособленец (шаблон проектирования) (HjnvhkvkQlyuye (ogQlku hjkytmnjkfgunx))
Приспособленец | |
---|---|
Flyweight | |
Тип | структурный |
Описан в Design Patterns | Да |
Приспособленец (англ. flyweight, «легковесный (элемент)») — структурный шаблон проектирования, при котором объект, представляющий себя как уникальный экземпляр в разных местах программы, по факту не является таковым.
Цель
[править | править код]Оптимизация работы с памятью путём предотвращения создания экземпляров элементов, имеющих общую сущность.
Описание
[править | править код]Приспособленец используется для уменьшения затрат при работе с большим количеством мелких объектов. При проектировании приспособленеца необходимо разделить его свойства на внешние и внутренние. Внутренние свойства всегда неизменны, тогда как внешние могут отличаться в зависимости от места и контекста применения и должны быть вынесены за пределы приспособленца.
Приспособленец дополняет шаблон фабрика таким образом, что при обращении клиента к фабрике для создания нового объекта ищет уже созданный объект с такими же параметрами, что и у требуемого, и возвращает его клиенту. Если такого объекта нет, то фабрика создаст новый.
Примеры
[править | править код]Пример на Python
[править | править код]
class Lamp(object):
def __init__(self, color):
self.color = color
class LampFactory:
lamps = dict()
@staticmethod
def get_lamp(color):
return LampFactory.lamps.setdefault(color, Lamp(color))
class TreeBranch(object):
def __init__(self, branch_number):
self.branch_number = branch_number
def hang(self, lamp):
print(f"Hang ${lamp.color} [${id(lamp)}] lamp on branch ${self.branch_number} [${id(self)}]")
class ChristmasTree(object):
def __init__(self):
self.lamps_hung = 0
self.branches = {}
def get_branch(self, number):
return self.branches.setdefault(number, TreeBranch(number))
def dress_up_the_tree(self):
self.hang_lamp("red", 1)
self.hang_lamp("blue", 1)
self.hang_lamp("yellow", 1)
self.hang_lamp("red", 2)
self.hang_lamp("blue", 2)
self.hang_lamp("yellow", 2)
self.hang_lamp("red", 3)
self.hang_lamp("blue", 3)
self.hang_lamp("yellow", 3)
self.hang_lamp("red", 4)
self.hang_lamp("blue", 4)
self.hang_lamp("yellow", 4)
self.hang_lamp("red", 5)
self.hang_lamp("blue", 5)
self.hang_lamp("yellow", 5)
self.hang_lamp("red", 6)
self.hang_lamp("blue", 6)
self.hang_lamp("yellow", 6)
self.hang_lamp("red", 7)
self.hang_lamp("blue", 7)
self.hang_lamp("yellow", 7)
def hang_lamp(self, color, branch_number):
self.get_branch(branch_number).hang(LampFactory.get_lamp(color))
self.lamps_hung += 1
if __name__ == '__main__':
ChristmasTree().dress_up_the_tree()
Пример на Python (с переопределением конструктора)
[править | править код]
class Lamp(object):
__instances = dict()
def __new__(cls, color):
return cls.__instances.setdefault(color, super().__new__(cls))
def __init__(self, color):
self.color = color
class TreeBranch(object):
def __init__(self, branch_number):
self.branch_number = branch_number
def hang(self, lamp):
print(f"Hang ${lamp.color} [${id(lamp)}] lamp on branch ${self.branch_number} [${id(self)}]")
class ChristmasTree(object):
def __init__(self):
self.lamps_hung = 0
self.branches = {}
def get_branch(self, number):
return self.branches.setdefault(number, TreeBranch(number))
def dress_up_the_tree(self):
for branch in range(1, 8):
for color in "red", "blue", "yellow":
self.hang_lamp(color, branch)
def hang_lamp(self, color, branch_number):
self.get_branch(branch_number).hang(Lamp(color))
self.lamps_hung += 1
if __name__ == '__main__':
ChristmasTree().dress_up_the_tree()
Пример № 1 на Java
[править | править код]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.
}
Пример № 2 на Java
[править | править код]public abstract class EnglishCharacter {
protected char symbol;
protected int width;
protected int height;
public abstract void printCharacter();
}
public class CharacterA extends EnglishCharacter {
public CharacterA(){
symbol = 'A';
width = 10;
height = 20;
}
@Override
public void printCharacter() {
System.out.println("Symbol = " + symbol + " Width = " + width + " Height = " + height);
}
}
public class CharacterB extends EnglishCharacter {
public CharacterB(){
symbol = 'B';
width = 20;
height = 30;
}
@Override
public void printCharacter() {
System.out.println("Symbol = " + symbol + " Width = " + width + " Height = " + height);
}
}
public class CharacterC extends EnglishCharacter {
public CharacterC(){
symbol = 'C';
width = 40;
height = 50;
}
@Override
public void printCharacter() {
System.out.println("Symbol = " + symbol + " Width = " + width + " Height = " + height);
}
}
public class FlyweightFactory {
private HashMap<Integer, EnglishCharacter> characters = new HashMap();
public EnglishCharacter getCharacter(int characterCode){
EnglishCharacter character = characters.get(characterCode);
if (character == null){
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;
}
}
/*
* Класс, показывающий работу шаблона проектирования "Приспособленец".
* */
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#
[править | править код]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 virtual void Display(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(this.symbol +
" (pointsize " + this.pointSize + ")");
}
}
// "ConcreteFlyweight"
class CharacterA : Character
{
// Constructor
public CharacterA()
{
this.symbol = 'A';
this.height = 100;
this.width = 120;
this.ascent = 70;
this.descent = 0;
}
}
// "ConcreteFlyweight"
class CharacterB : Character
{
// Constructor
public CharacterB()
{
this.symbol = 'B';
this.height = 100;
this.width = 140;
this.ascent = 72;
this.descent = 0;
}
}
// ... 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;
}
}
}
Пример на C++
[править | править код]#include <map>
#include <iostream>
#include <memory>
// "Flyweight"
class Character
{
public:
virtual ~Character() = default;
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;
mWidth = 120;
mHeight = 100;
mAscent = 70;
mDescent = 0;
mPointSize = aPointSize;
}
// from Character
virtual void display() const {
std::cout << mSymbol << " ( PointSize " << mPointSize << " )\n";
}
};
// "FlyweightFactory"
template < const int POINT_SIZE >
class CharacterFactory
{
public:
const Character& getCharacter( char aKey )
{
// Uses "lazy initialization"
Characters::const_iterator it = mCharacters.find( aKey );
if ( mCharacters.end() == it ) {
mCharacters[aKey] = std::make_unique<const ConcreteCharacter>(aKey, POINT_SIZE);
return *mCharacters[aKey];
} else {
return *it->second;
}
}
private:
using Characters = std::map < char, std::unique_ptr<const Character> >;
Characters mCharacters;
};
int main(){
std::string document = "AAZZBBZB";
CharacterFactory<12> characterFactory;
for (auto it :document){
auto&& character = characterFactory.getCharacter( it );
character.display();
}
return 0;
}
Пример на PHP5
[править | править код]<?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
[править | править код]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
Пример на Ruby
[править | править код]# Объект Приспособленец
class Lamp
attr_reader :color
#attr_reader makes color attribute available outside
#of the class by calling .color on a Lamp instance
def initialize(color)
@color = color
end
end
class TreeBranch
def initialize(branch_number)
@branch_number = branch_number
end
def hang(lamp)
puts "Hang #{lamp.color} lamp on branch #{@branch_number}"
end
end
# Flyweight Factory
class LampFactory
def initialize
@lamps = {}
end
def find_lamp(color)
if @lamps.has_key?(color)
# if the lamp already exists, reference it instead of creating a new one
lamp = @lamps[color]
else
lamp = Lamp.new(color)
@lamps[color] = lamp
end
lamp
end
def total_number_of_lamps_made
@lamps.size
end
end
class ChristmasTree
def initialize
@lamp_factory = LampFactory.new
@lamps_hung = 0
dress_up_the_tree
end
def hang_lamp(color, branch_number)
TreeBranch.new(branch_number).hang(@lamp_factory.find_lamp(color))
@lamps_hung += 1
end
def dress_up_the_tree
hang_lamp('red', 1)
hang_lamp('blue', 1)
hang_lamp('yellow', 1)
hang_lamp('red', 2)
hang_lamp('blue', 2)
hang_lamp('yellow', 2)
hang_lamp('red', 3)
hang_lamp('blue', 3)
hang_lamp('yellow', 3)
hang_lamp('red', 4)
hang_lamp('blue', 4)
hang_lamp('yellow', 4)
hang_lamp('red', 5)
hang_lamp('blue', 5)
hang_lamp('yellow', 5)
hang_lamp('red', 6)
hang_lamp('blue', 6)
hang_lamp('yellow', 6)
hang_lamp('red', 7)
hang_lamp('blue', 7)
hang_lamp('yellow', 7)
puts "Made #{@lamp_factory.total_number_of_lamps_made} total lamps"
end
end
Символы на Smalltalk
[править | править код]Символы в Smalltalk практически идентичны «обычным строкам», но не порождаются каждый раз заново. Два идентичных символа на самом деле всегда являются одним и тем же экземпляром класса Symbol, тогда как две идентичные строки могут быть разными экземплярами класса String.
Ссылки
[править | править код]- Паттерн Flyweight (приспособленец) — назначение, описание, особенности и реализация на C++.