Flyweight (design sample)

from Wikipedia, the free encyclopedia

The Flyweight ( English flyweight pattern ) is a proven, reusable solution template in the software development and is one as a template to the problems of the category structure pattern ( structural patterns ). The more general design patterns and also this template for relationships between similar objects come from the book Design Patterns of the "Gang of Four" (GoF), which explains techniques of object-oriented programming .

implementation

The flyweight is used when a large number of similar objects is required that share variable information. A classic implementation would require a disproportionately large amount of storage capacity and lead to problems during runtime . Part of the state of these objects can be outsourced into context , in an extrinsic way, stimulated from the outside. After the state has been removed, the number of different objects is reduced to a manageable level.

Class diagram
This diagram in the Unified Modeling Language (UML) shows the relationship from a customer (client) to the "factory" to the flyweight.

The individual elements are:

  • The client ( client ) manages references to flyweights and the extrinsic state of flyweights.
  • The flyweight factory ( flyweight factory ) creates and manages flyweights. It ensures the correct use of the shared objects.
  • The flyweight is an abstract class and defines the interface for objects that receive and process an externally visible state.
  • The Concrete flyweight ( concrete flyweight ) implements the Flyweight interface. If necessary, an internal state is added. Instances of KonkretesFliegengewichtor derived classes are shared. The intrinsic state must be independent of the context.
  • The separately used concrete flyweight ( unshared concrete flyweight ) also implements this interface, but contains the complete status. This means that these objects are not shared. These are no longer flyweights in the strict sense. It can even hide real "heavyweights" behind it. Rather, it shows the point at which "normal" objects find their place in the pattern.

Advantages and disadvantages

The method reduces storage costs proportionally to the size of the outsourced state and the number of flyweights. The storage costs are further reduced if the relocated state does not have to be saved, but can be calculated.

On the other hand, the complexity increases relatively sharply, especially in designs that use flyweight together with composite . A clear documentation of the responsibilities is a must. The runtime costs increase because the outsourced state has to be found again and transferred to the flyweight when the method is called. They keep rising as the state is calculated.

Examples

One example is the graphical representation of a text document that can easily consist of hundreds of thousands or even millions of characters and thus drawing objects. Each byte in the drawing object may become one megabyte . It is unacceptable to actually store all of the information that the drawing object needs in the object.

The drawing object is located in a line object ( compound word ). The line number and the Y coordinate on the screen are identical for all characters in the line. They are moved to the row object. The column number and the X coordinate result from the position in the line. The row object is responsible for calculating this. Font attributes are usually the same for neighboring characters. They are also outsourced.

example

The only thing left is the code of the character. So in the end there are only a few hundred different drawing objects, at least with alphabet fonts .

The typical code in the Java programming language for billing at a coffee bar contains the following elements:

// Flyweight object interface
public interface CoffeeOrder {
    public void serveCoffee(CoffeeOrderContext context);
}
// ConcreteFlyweight object that creates ConcreteFlyweight
public class CoffeeFlavor implements CoffeeOrder {
    private String flavor;

    public CoffeeFlavor(String newFlavor) {
        this.flavor = newFlavor;
    }
    public String getFlavor() {
        return this.flavor;
    }
    public void serveCoffee(CoffeeOrderContext context) {
        System.out.println("Serving Coffee flavor " + flavor + " to table number " + context.getTable());
    }
}
public class CoffeeOrderContext {
   private int tableNumber;

   public CoffeeOrderContext(int tableNumber) {
       this.tableNumber = tableNumber;
   }
   public int getTable() {
       return this.tableNumber;
   }
}
import java.util.HashMap;
import java.util.Map;

//FlyweightFactory object
public class CoffeeFlavorFactory {
    private Map<String, CoffeeFlavor> flavors = new HashMap<String, CoffeeFlavor>();

    public CoffeeFlavor getCoffeeFlavor(String flavorName) {
        CoffeeFlavor flavor = flavors.get(flavorName);
        if (flavor == null) {
            flavor = new CoffeeFlavor(flavorName);
            flavors.put(flavorName, flavor);
        }
        return flavor;
    }
    public int getTotalCoffeeFlavorsMade() {
        return flavors.size();
    }
}
public class TestFlyweight {
   /** The flavors ordered. */
   private static CoffeeFlavor[] flavors = new CoffeeFlavor[100];
   /** The tables for the orders. */
   private static CoffeeOrderContext[] tables = new CoffeeOrderContext[100];
   private static int ordersMade = 0;
   private static CoffeeFlavorFactory flavorFactory;

   public static void takeOrders(String flavorIn, int table) {
       flavors[ordersMade] = flavorFactory.getCoffeeFlavor(flavorIn);
       tables[ordersMade++] = new CoffeeOrderContext(table);
   }
   public static void main(String[] args) {
       flavorFactory = new CoffeeFlavorFactory();
      /** Durch Zwischenspeicherung der Geschmäcker in einer HashMap in der Factory wird jeweils nur ein Objekt des gleichen Geschmacks erzeugt und damit Speicherplatz gespart. */
       takeOrders("Cappuccino", 2);
       takeOrders("Cappuccino", 2);
       takeOrders("Frappe", 1);
       takeOrders("Frappe", 1);
       takeOrders("Xpresso", 1);
       takeOrders("Frappe", 897);
       takeOrders("Cappuccino", 97);
       takeOrders("Cappuccino", 97);
       takeOrders("Frappe", 3);
       takeOrders("Xpresso", 3);
       takeOrders("Cappuccino", 3);
       takeOrders("Xpresso", 96);
       takeOrders("Frappe", 552);
       takeOrders("Cappuccino", 121);
       takeOrders("Xpresso", 121);

       for (int i = 0; i < ordersMade; ++i) {
           flavors[i].serveCoffee(tables[i]);
       }
       System.out.println(" ");
       System.out.println("total CoffeeFlavor objects made: " +  flavorFactory.getTotalCoffeeFlavorsMade());
   }
}

Application in analysis

The flyweight is a pure design model, as its application is primarily driven by the design aspect of storage space , as well as the aspect of central update of a very global state . The use of flyweight in the analysis is usually a code smell (German 'bad smell'), which suggests a revision of the poorly structured program source code.

The compound can be used to combine flyweights into hierarchical structures, e.g. B. character, line, paragraph. A factory method is needed to produce the flyweights.

The flyweight pattern is also advantageous for the state and strategy objects. The idiom “immutable object” is closely related to the flyweight. Flyweights should always be designed as "immutable objects".

Individual evidence

  1. Erich Gamma , Richard Helm , Ralph Johnson , John Vlissides : Design pattern . 5th edition. Addison-Wesley, 1996, ISBN 3-8273-1862-9 , pp. 223 .
  2. ^ Karl Eilebrecht, Gernot Starke: Patterns compact . 4th edition. Springer Vieweg Verlag, Berlin 2013, ISBN 978-3-642-34717-7 , p. 98 .