Context:
Gamma's definition of Design Patterns:
- Design patterns systematically names, explains and evaluates an important and recurring design in object-oriented systems.
- Their main objective is to reuse successful designs and architectures of experienced and professional designers.
- The best practices found in successful architectures are codified as design patterns.
- The ideas in design patterns can be used in other stages of the software development also.
Most of the important works in this field are due to:Gamma, Helm, Johnson, Vlissides. Small Talk came up with Model/View/Control (MVC). Analysis Patterns by Fowler which are very useful during the initial stages of the software development. Architectural Patterns by Mary Shaw, Garlan, Schmidt.
- Simple and elegant solutions to specific, commonly occuring problems in software design.
- Each pattern describes a problem that occurs over and over again abd outlines a solution for the problems in a way that the solution can be reused in numerous contexts.
Catalog of Design Patterns:Design patterns can be classified into different families based on two criteria:Purpose: This reflects what a pattern does. The three classifications of design patterns based on purpose are:
Scope: This specifies whether the pattern applies primarily to classes or objects. Based on scope criterion, patterns are organized into:
- Creatiional patterns: concerned with the creation of objects.
- Structural patterns: concerned with how to assemble/compose objects to get interesting functionality.
- Behavioural patterns: deals with the different ways in which classes and objects interact with one another and distribute responsibility.
- Class Patterns:
- They deal with relationship between classes and their subclasses.
- Since these relationships are established through inheritance, they are static- fixed at compile time.
- Object Patterns:
- Relationships can be changed at run-time and are more dynamic.
The table given below summarizes the design pattern space.
Scope\Purpose | Creational Pattern | Structural Pattern | Behavioural Pattern |
Class | Factory Method | Adapter(class) | Interpreter
Template Method |
Object | Abstract Factory
Builder Prototype Singleton |
Adapter(object)
Bridge Composite Decorator Facade Flyweight Proxy |
Chain of Resposibility
Command Iterator Medicator Memento Observer State Strategy Visitor |
Describing Design Patterns:
Pattern Name and Classification: A good name is vital since it conveys the essence of the pattern.Intent: The purpose of the pattern, its rationale and the design issues it address are stated.
Also Known As: Other well known names of the pattern, if any.
Motivation: A scenario that illustrates a design problem is presented and shows how the class and object structures in the pattern solves it.
Applicability:The situations where the pattern can be used and how to identify those situations are described.
Structure: A graphical representation of the classes in the pattern using the notaion based on the Object Modelling Technique(OMT). Also interaction diagrams can be used to illustrate the sequences of requests and collaborations between objects.Participants: The classes and/or the objects participating in the design pattern and their responsibilities.
Collaborations: How the participants collaborate to carry out their responsibility.
Consequences:
How does the pattern support its objectives?
What are the trade offs and results of using this pattern?
What aspect of the system structure does it let you to vary independently?Implementation:
What pitfalls, hints, or techniques should you be aware of when implementing the pattern?
Are there language-specific issues?
Sample Code: Code fragments that illustrate how to implement the pattern.Related Patterns:
What design patterns are closely related to this one?Known Uses: Examples of the pattern found in real systems.
What are the important differences?
With which other patterns should this one be used?
Why Design Patterns?
Enable best practises to be used commonly.use composition over inheritance. (Patterns like Composite, Decorator, COR, mediator, Observer, Strategy etc uses this practise)Find appropriate objects during design.
program to interface rather than implementation.
use interface inheritance rather than implementation inheritance.
use a judicious mix of composition and inheritance.The task of decomposing a system into objects is influenced by a number of factors such as encapsulation, granularity, dependency, flexibility, performance, ....Determine the granularity of the object.
Design patterns help to identify the less obvious abstractions and objects during the design phase of the software development process.
Example: Composite and Strategy.Granularity refers to the level of detail to be included with an object.Specify object interfaces.
An object can represent everything down to the hardware or all the way up to entire applications.
Design patterns like Facade and Flyweight addresses this issue of granularity.The interface of an object represents the set of requests that can be sent tot the object.
Design patterns help to identify the key elements in the interface, the kind of data exchanged and the relationships between interfaces.
Example: Memento.
Specify object implementation.Design patterns help to ensure that the system is written in terms of interface, not in terms of implementation.Effective use of reuse mechanism.
Example: Chain of Responsibility, Composite, Strategy, State, Command, Observer.Inheritance
- Inheritance results in white-box reuse where the internals of the parents are visible to the subclasses. Hence inheritance breaks encapsulation.
- It is defined statically at compile-time and hence implementations cannot be changed at run time.
- It makes it easier to modify the implementation to be reused.
- It can lead to explosion of subclasses.
Composition
- New functionality is obtained by assembling objects.
- Composition leads to black-box reuse since the internal details of the objects are not visible.
- It is dynamic in nature and hence implementation can be changed at run time.
- A design based on object composition have more objects and inter-relationships between them instead of being defined in a single class.
Delegation
- Helps to make composition as powerful a way for reuse as inheritance by composing behaviors at run time.
- Two objects are involved in handling a request: a receiving object and a delegate.
- The receiving object delegates operations to the delegate and passes itself to the delegate so that the delegated operation can refer to the receiver.
- Dynamically highly parameterized software is harder to understand.
- Run time inefficiencies also exist.
- Design patterns like State, Strategy, Visitor, Mediator etc. uses delegation.
Parameterized types
- Also called generics or templates.
- It helps to define a type without specifying all the other types it uses. The unspecified types are provided as parameters. For example, if we define a new type as List, a list of integers can be created by passing "int" as parameter and for creating a string list "string" is passed as parameter.
- It cannot change at run time.
Relate run-time and compile-time structures better.Helps to come up with excellent documentation.It is often very difficult to find a one-one mapping between run-time structure and code structure. Design patterns are very helpful in capturing the distinction between the compile-time and run-time structures explicitly. They facilitates reverse engineering. Example: Composite, Chain of Responsibility, Decorator, Observer. Enable design for change.
- Create objects indirectly: Abstract Factory, Factory Method, Prototype.
- Limit dependence on specific operations: Chain of Responsibility, Command.
- Limit platform dependencies: Abstract Factory, Bridge.
- Encapsulation of implementation details from the clients: Abstract Factory, Bridge, Memento, Proxy.
- Limit Algorithmic dependencies: Builder, Iterator, Strategy, Template, Method, Visitor.
- Greater flexibility can be obtained by making the classes loosely coupled: Abstract Factory, Bridge, Chain of Responsibility, Command, Facade, Mediator, Observer.
- Avoid too many levels of subclassing: Bridge, Chain of Responsibility, Composite, Decorator, Observer, Strategy.
- Ability to alter classes conveniently: Adapter, Decorator, Visitor.
Helps in the design of application programs, toolkits and frameworks.
- It enables the design of high quality application programs by
- Promoting internal reuse by reducing dependencies.
- Enhancing extensibility by exploiting composition and reduced coupling.
- Making the application more maintainable.
- Toolkit is a set of related and reusable classes to provide useful and general purpose functionality. It enables reuse of code. (Example: set of classes for stacks, lists etc.) Dependencies and assumptions that limit the flexibility and applicability of the toolkit should be avoided.
- A framework is a set of cooperating classes that make up a reusable design for a particular domain(Example: finance, graphics ...). Abstract classes will be present and to customize a particular application, create application-specific subclasses. Here, the emphasis is on design reuse. Framework results in the fast development of the application, but much of the creative freedom is lost since most of the design decisions are made by it. The key points to be noted in the design of framework are extensibility, flexibility and reduced coupling. Inspite of the similarities between design patterns and framework, they differ in three major ways.
- Design patterns are more abstract than framework: A framework can be reused whereas design patterns have to be implemented each time they are used.
- Design patterns are smaller architectural elements than framework: A framework can include several design patterns while the reverse is never true.
- Design patterns are less specialized than framework: A framework always has a particular application domain.
Document Editor: A Case Study
Requirements Specification:Document Structure:1. Document Structure: Treat characters, lines, rows and columns uniformly.
2. Formatting: It should be possible to dynamically switch between different formatting algorithms.
3. Embellish the user interface in a very flexible and dynamic way.
4. Support multiple look and feel standards: Lexi should easily adapt to multiiple look and feel standards like Motif, PM etc.
5. Support multiple windowing systems: Lexi should be independent of the windowing systems used.
6. Support flexible user operations.
7. Spell check and hyphenation.The resulting object structure is as shown below.A document is an arrangement of basic graphical elements such as characters, lines, polygons and other shapes. The basic idea used to compose a document is that of recursive composition. Recursive composition is the technique by which we build complex elements out of simpler ones. It uses a mix of composition and inheritance. For example, a set of characters and graphics are arranged to form a row, multiple rows are arranged to form a column, multiple columns can form a page and so on. The design pattern, Composite captures the essence of recursive composition in object-oriented terms.
Formatting:We define an abstract class, Glyph, to represent all the objects that can appear in a document. It provides three operations- Draw(Window) draws itself in the window, Intersects(Point) returns checks whether the glyph is intersecting with the point p and Insert(Glyph, int) inserts the Glyph at position 'int'. The partial Glyph class hierarchy is shown below. Here Character, Polygon and Rectangle are the leaves and Row represents the composite.
Embellishing User Interface:Usually different formatting algorithms are used within the same document structure. It should be possible to switch between different algorithms. An important trade-off to consider in this case is the balance between formatting speed and formatting quality. The formatting algorithms should be completely independent of the document structure ie, they should not change with change in the document structure. Conversely, addition of a new algorithm shouldn't require the modification of the existing glyphs. Main idea used here is the encapsulation of algorithms using objects. Strategy is best suited for encapsulating algorithms and dynamically switching between them. In the class diagram show below, whenever an object of type Composition is created, a Compositor is also created, which is responsible for choosing tha appropriate algorithm. Here Composition is the context, Compositor is the strategy and the concrete strategies are ArrayCompositor, TeXCompositor and SimpleCompositor.
Monoglyph serves as an abstract class for embellishment glyphs such as borders and scrollbars. The resulting object structure is as shown below.It should be possible to add and remove embellishments dynamically. The main idea used is that of transparent enclosure. This concept combines the notions of single child composition and compatible interfaces. The design pattern, Decorator, gives emphasis to transparent enclosure and single child composition. Embellishments such as borders and scrollbars can be added to the above document editor as shown in the class diagram below. Here the decorator is Monoglyph and the concrete decorators are Border and ScrollBar. Responsibilities can be easily added and detached dynamically using the structure given below.
Adding multiple look and feel standards:Supporting multiple window systems:It should be possible to support multiple look and feel standards like Presentation Manager, Motif, Mac. Each of these provide standard scrollbars, buttons and menus. Main idea used here is to postpone the creation of objects till they are required and to create them indirectly. Abstract Factory can be used for abstracting the process of object creation. Normally, we create an instance of the MotifFactory class using the following code:
ScrollBar *sb = new MotifScrollBar;Here, the client is tied to a particular look and feel standard namely, Motif. This can be avoided using the following code.
ScrollBar *sb = guiFactory->CreateScrollBar();
GUIFactory *guiFactory = new MotifFactory;
The resulting GUIFactory hierarchy is as shown below. The Abstract Factory is WidgetFactory, the concrete factories are MotifWidgetFactory and PMWidgetFactory and the abstract products are Window and Scrollbar.
There can be multiple windowing systems such as Windows, X, Presentation Manager etc. Abstractions in this case are not as clear as in the case of look and feel standards. Hence Abstract Factory is not a good choice. Bridge pattern helps to create two separate class hierarchies, one that supports the logical notion of windows and the other for capturing the different implementations of the windows. It isolates abstraction from implementation. The different operations, the window class should support are:
- Window management:
virtual void redraw()
virtual void raise()
virtual void lower()
virtual void iconify()
virtual void deiconify()- graphics:
virtual void DrawLine()
virtual void DrawRect()
virtual void DrawPolygon()
virtual void DrawText()Two extreme philosophies can be considered:make the interface extremely rich with all the functionalities.take the minimum common functionality. Neither extreme is a viable solution, so the window class will provide a convenient interface that supports the most popular features. The resulting hierarchy of window abstractions separating the abstraction from implementation is as follows. The Abstraction is Window, Implementor is WindowImp, the concrete implementors are XwindowImp and PMWindowImp and the RefinedAbstractions are IconWindow and TransientWindow. User operations:
Spell Checking and Hyphenation:
- The document editor should provide functionalities like
- creating a new document.
- open a document.
- save a document.
- print a document.
- cut and paste.
- fonts.
- alignment and justification.
- undo and redo.
- quit ...
- A lot of other factors are also to be considered like no undo for save or open, which are operation specific.
- For doing undo and redo, we need to keep track of the states. To store the state, we can use objects since each object has an identity, state and behavior.
- Define each operation as a command.
- The pattern Command can be used in which the emphasis is on encapsulating variation.
- The partial Command class hierarchy is as shown below.
The figure below shows the illustration of Command.
The usage of Command is depicted by the following figure.
- Textual Analysis is to be done to check for misspellings and introducing hyphenation points.
- A diverse set of algorithms exist for spell checking and hyphenation.
- Two factors are to be considered:
- Accessing the information to be analyzed which is scattered over the glyphs in the document structure.
- Should handle different data structures used for storing the objects.
- Support different kinds of traversals like preorder, postorder and inorder.
- Doing the analysis.
- The design pattern, Iterator can be used to provide a general interface for access and traversal.
- Subclasses can be created for handling different data structures like arrays and lists and for handling different traversals like preorder, postorder etc.
- The operations like First(), Next() and IsDone () are used for the traversal.
- The resulting Iterator classes and subclasses are as follows.
- It is better to keep the traversal and analysis phases separate because most often different analyses require the same kind of traversal.
- Analysis can be done using the design pattern, Visitor.
- Visitor class represents an abstract interface for visiting glyphs in a structure.
- Concrete subclasses of Visitor like SpellCheckerVisitor and HyphenationVisitor perform different analyses like spell checking and hyphenation.
- The class, Glyph includes an operation Accept(Visitor v).
- When it accepts a visitor, it sends a request to the visitor with the Glyph element as argument.
- The visitor will then execute the operation for that element- spell checking and hyphenation.
- Adding a new analysis requires just defining a new subclass of Visitor.