Pattern Flyweight

Pattern Name

Pattern Flyweight

Intent

Utilizzare la condivisione per supportare in modo efficiente un gran numero di oggetti a granularità fine.

Motivation (Forces)

L'abuso di piccoli oggetti ripetuti può dare problemi di memoria. Perciò può essere utile utilizzare una sola istanza condivisa.

Applicability

Structure

Participants

  • Flyweight - Dichiara un'interfaccia attraverso la quale i flyweight possono ricevere e agire su stati esterni
  • ConcreteFlyweight - Implementa l'interfaccia Flyweight e aggiunge salvataggio per lo stato intrinseco, se ce n'è uno. Un ConcreteFlyweight deve essere condivisibile. Qualsiasi stato che salva deve essere intrinseco, ovvero indipendente dal contesto d'uso del ConcreteFlyweight.
  • UnsharedConcreteFlyweight - Non tutte le sottoclassi di Flyweight devono essere condivise. L'interfaccia lo permette ma non obbliga. E' comune per UnsharedConcreteFlyweight avere dei figli ConcreteFlyweight ad un certo livello nella struttura di Flyweight
  • FlyweightFactory - Crea e gestisce gli oggetti Flyweight. Assicura che i flyweight siano condivisi in modo corretto.Quando un client richiede un flyweight, la FlyweightFactory fornisce una istanza di un elemento esistente o ne crea uno nuovo, se non esiste.
  • Client - Mantiene i riferimenti ai Flyweight. Calcola o salva lo stato estrinseco dei flyweight.

Consequences

  • Poche istanze
  • Poca memoria usata
  • Stato calcolabile e non memorizzato

Implementation

  1. Rimozione dello stato esterno
  2. Gestione degli oggetti condivisi

Sample Code

Java
            public enum FontEffect {
            BOLD, ITALIC, SUPERSCRIPT, SUBSCRIPT, STRIKETHROUGH
            }

            public final class FontData {
            /**
            * A weak hash map will drop unused references to FontData.
            */
            private static final WeakHashMap<FontData, FontData>flyweightData = new WeakHashMap<FontData, 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);
            for (FontEffect fontEffect : effects) {
            effectsSet.add(fontEffect);
            }
            // 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, data);
            }
            // return the single immutable copy with the given values
            return flyweightData.get(data);
            }

            @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.
            }
          
C#
            using System.Collections;
            using System.Collections.Generic;
            using System;

            class GraphicChar {
            char c;
            string fontFace;
            public GraphicChar(char c, string fontFace) { this.c = c; this.fontFace = fontFace; }
            public static void printAtPosition(GraphicChar c, int x, int y)   {
            Console.WriteLine("Printing '" + c.c + "' in '{1}' at position {2}:{3}.", c.c, c.fontFace, x, y);
            }
            }

            class GraphicCharFactory {
            Hashtable pool = new Hashtable(); // the Flyweights

            public int getNum() { return pool.Count; }

            public GraphicChar get(char c, string fontFace) {
            GraphicChar gc;
            string key = c.ToString() + fontFace;
            if ((gc = (GraphicChar)pool[key]) == null) {
            gc = new GraphicChar(c, fontFace);
            pool.Add(key, gc);
            }
            return gc;
            }
            }

            class FlyWeightExample {
            public static void Main(string[] args) {
            GraphicCharFactory cf = new GraphicCharFactory();

            // Compose the text by storing the characters as objects.
            List<GraphicChar> text = new List<GraphicChar>();
            text.Add(cf.get('H', "Arial"));    // 'H' and "Arial" are called intrinsic information
            text.Add(cf.get('e', "Arial"));    // because it is stored in the object itself.
            text.Add(cf.get('l', "Arial"));
            text.Add(cf.get('l', "Arial"));
            text.Add(cf.get('o', "Times"));

            // See how the Flyweight approach is beginning to save space:
            Console.WriteLine("CharFactory created only {0} objects for {1} characters.", cf.getNum(), text.Count);

            int x=0, y=0;
            foreach (GraphicChar c in text) {             // Passing position as extrinsic information to the objects,
            GraphicChar.printAtPosition(c, x++, y);   // as a top-left 'A' is not different to a top-right one.
            }
            }
            }
          

Known Uses

Icone di Windows

Related Patterns

  • Composite
  • State/Strategy