A Survey of Common Design Patterns, Page 3
The Flyweight pattern is useful for situations where you have a small number of different objects that might be needed a very large number of times—with slightly different data that can be externalized outside those objects. The Flyweight is intended to use sharing to support large numbers of fine-grained objects more efficiently and reduce resource usage. The pattern makes reference to an object's intrinsic data, that makes it unique and extrinsic data, that gets passed in as parameters. This pattern is useful for applications in which you may need to display icons to represent folders or some other object and don't want to add the overhead of creating new icons for each individual folder. An example would be the right pane of Microsoft Windows Explorer. In this type of situation, it may be better to share instances of a class. This pattern differs from a Singleton because you can have a small number of Flyweights, such as one for every different icon type.
In the next class diagram, the Flyweight class is abstract and defines an interface for all Flyweights to implement behavior to act on extrinsic data. The Concrete Flyweight implements the Flyweight interface and maintains the intrinsic data. A ConcreteFlyweight object must be sharable. Any data it stores must be intrinsic; that is, it must be independent of the ConcreteFlyweight object's context. Not all Flyweight subclasses need to be shared, so you can also create classes for unshared flyweight data, as well. The client is responsible for maintaining references to the flyweight objects and the extrinsic data (data that makes the instance unique).
Some applications provide support for built-in scripting and macro languages so users can describe operations they can perform in the application. The Interpreter is intended to provide you with a way to define a representation of the grammar of a language with an interpreter that uses the representation to interpret sentences in the language.
The following class diagram shows that you define two types of classes. A Context is responsible for the data that is accessible to the Interpreter. You also define an abstract Expression class that declares an abstract Interpret operation. Terminal and Nonterminal concrete classes are defined that inherit the abstract Expression interface. They provide the implementation for the Interpet(...) function, which is responsible for the interpret operation to be performed on the terminal and nonterminal tokens, respectively, for the abstract tree syntax. In the case of nonterminal expressions, you need to define a class for each rule in your grammar. The client is responsible for creating the abstract syntax tree using the terminal and nonterminal expression objects as appropriate, and calling the interpret(...) method. Because the different expressions conform to the expression interface, the client can interpret the complete expression without knowing the structure of the abstract tree.
The Iterator pattern provides a client with a way to access the elements of an aggregate object sequentially without having to know the underlying representation. Iterator also provides you with a way to define special Iterator classes that perform unique processing and return only specific elements of the data collection. The Iterator is especially useful because it provides the client with a common interface so it doesn't need to know anything about the underling data structure.
The following class diagram shows the Iterator pattern consists of a Node Collection, a Node class, and the Iterator classes. An abstract Iterator class provides a uniform interface for the various concrete Iterator classes to implement. The client creates instances of the Node collection and adds Nodes objects as appropriate. Finally, it creates an instance of the appropriate Iterator and uses it to traverses the node collection accordingly. With this technique, you can build a family of generic methods for traversing node collections.
Applications with many classes tend to become brittle as communication between them becomes more complex. The more classes know about each other, the more difficult it becomes to change the software. Mediator is intended to define an object that encapsulates how a set of objects interacts. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and lets you vary their interaction independently.
The next class diagram shows that Mediator consists of two types of classes, Colleague and Mediator. Colleagues are the objects from your application that you want to interact, and Mediators are responsible for defining the interactions. In the Mediator class, you define the methods that act on the Colleague objects and a method for introducing Colleagues. The client is responsible for instantiating the appropriate Colleague objects and the Mediator, and passing the Colleague objects to the Mediator's IntroduceColleagues(...) method. The client can then invoke the appropriate method on the Colleague objects. The Mediator assures the requests are passed to the interacting Colleagues. The main benefit of Mediator is that the interactions between relating objects are removed from the objects themselves and localized in the Mediator.
Client code often needs to record the current state of an object, without being interested in the actual data values (for example,, supporting undo operations). To support this behavior, we can have the object record its internal data in a helper class called a Memento, and the client code can treat this object like a black box for storing its current state. Then, at some later point in time, the client can pass the Memento back into the object, to restore it to a previous state. The Memento provides you with a way to capture and externalize an object's internal state so that it can be restored at a later time. It does this by violating encapsulation and without making the object responsible for this capability itself.
The class diagram for memento (see the next figure) shows the Originator as the object whose state you want to persist. You need to define the Memento class, which is responsible for storing the internal state or the Originator object. Finally, you define a Caretaker class that is responsible for protecting the Memento object. The key to Memento is that the Originator defines methods for creating and setting the Memento. In addition, Memento objects implement dual interfaces: one for the Originator and one for the Caretaker. Caretaker sees a narrow interface to the Memento—it can only pass the Memento to other objects. Originator, in contrast, sees a wide interface, one that lets it access all the data necessary to restore itself to its previous state. Ideally, only the originator that produced the memento would be permitted to access the memento's internal state. The client decides when it wants to store the state of the originator. It does so by invoking the Originators CreateMemento(...) method. The state can be restored by invoking SetMemento(...).
Page 3 of 5