CSC/ECE 517 Fall 2012/ch2b 2w35 av
One of the most complex tasks in creating a software is designing it to meet the needs of the user. Designing an object-oriented components of software that is reusable is hard. One must find pertinent objects, factor them into classes at the right granularity, define class interfaces and inheritance hierarchies, and establish key relationships among them. It should also be taken care that the design is specific to a problem at hand and also general enough to accommodate future changes. Knowledge of design patterns helps in such scenarios. Essentially, Design patterns describe a problem which occurs over and over again in an environment, and then describes the core of the solution to that problem, in such a way that one can use this solution a million times over, without ever doing it the same way twice
Classes of Design Patterns
Design patterns are generally classified into the one of the following three categories.
Creational Pattern, which help create the objects for the user, instead of having the user to instantiate the object.
Structural Pattern, which employ interfaces to achieve inheritance to enable objects to obtain new functionality.
Behavioral Pattern, which are concerned with communication between objects.
The intent of Builder pattern is to separate the construction of a complex object from its representation so that the same construction process can create different representations. According to GOF , the builder pattern can be applied when
1. The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled.
2. The construction process must allow different representations for the object that's constructed.
Let us consider an example for builder pattern. Consider a word processing software that should have the ability to convert Microsoft word's DOC to many text formats. The reader might convert *.DOC documents into plain ASCII text or into a PDF. The problem, however, is that the number of possible conversions is open-ended. So it should be easy to add a new conversion without modifying the reader.
A solution is to configure the DOCReader class with a TextConverter object that converts DOC to another textual representation. As the DOCReader parses the DOC document, it uses the TextConverter to perform the conversion. Whenever the DOCReader recognizes an DOC token (either plain text or an RTF control word), it issues a request to the TextConverter to convert the token. TextConverter objects are responsible both for performing the data conversion and for representing the token in a particular format. Subclasses of TextConverter specialize in different conversions and formats. For example, an PlainTextConverter ignores requests to convert anything except plain text. A LaTeXConverter, on the other hand, will implement operations for all requests in order to produce a LaTeX representation that captures all the stylistic information in the text.
Each kind of converter class takes the mechanism for creating and assembling a complex object and puts it behind an abstract interface. The converter is separate from the reader, which is responsible for parsing an DOC document. Converter class designed as discussed above basically defines the builder design pattern.
Abstract factory method, also known as Kit, provide an interface for creating families of related or dependent objects without specifying their concrete classes. The major difference between Abstract factory and builder pattern is that the focus with builder pattern is on 'step-by-step' object creation but with abstract factory, there is no step by step object creation. The object that is being created is usually created immediately and returned to the caller. The client software creates a concrete implementation of the abstract factory class and then uses the generic interfaces defined by the abstract factory to create the concrete objects according to the requirement.
Consider a case where we need to define a different look and feel for different operating systems or profiles. Hard coding them into each type can result in code duplication and also a poor quality code. This is where abstract factory pattern comes to use. A common factory class can be created for declaring an interface for creating each look and feel. Then, a concrete class can be created for each type of look and feel that we want and just implementing the required effects in the interfaces. An example of using Abstract factory to define different widget interfaces is shown here.
Here in this example, the MotifWidgetFactory has its own implementation of the createScrollBar and createWindow functions that instantiate MotifWindow and MotifScrollBar. This allows any number of newer types of objects to be created by just extending the abstract class and implementing the desired features. The clients call these operations to obtain the widget instances, but they'll never know which concrete classes have been used to create them. All the operations that are done by the client is done using the Abstract factory interface.
Abstract factory pattern helps in achieving the following,
- a system should be independent of how its products are created, composed, and represented.
- a system should be configured with one of multiple families of products.
- a family of related product objects is designed to be used together, and you need to enforce this constraint.
- you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
In factory method, a class defines interface for creating an object but lets the subclasses to decide which class to instantiate. Factory methods are used in the following situations.
- a class can't anticipate the class of objects it must create.
- a class wants its subclasses to specify the objects it creates.
- classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.
In this method, there exists an Abstract Creator class, that is implemented by a ConcreteCreator class. The problem occurs when the ConcreteCreator has to create an object of Product class, but doesn't know the exact type of product is required by the object of ConcreteCreator. Such situations occur when there is no proper relation between the Product and Creator. So, it will not be possible to say that a particular type of Creator always requires a particular type of Product. So a method in ConcreteCreator is used to create an object of the required type and is therefore called a Factory method.
The major difference between Builder pattern and Factory method is that, like Abstract factory there is no step by step process for the creation of an object. Factory method also provides a good way to connect parallel class hierarchies by allowing clients to access the factory methods for creating objects. A major advantage of using this methods is that, the factory methods can be as parameterized methods thus allowing to create a variety of different types of object as required by the Creator.
Prototype method allows to specify the kinds of objects to create using a prototypical instance and new objects can be created by copying this prototype. This approach is mainly useful when the type of object will not be known until runtime. Some areas where prototyping proves useful are,
- when the classes to instantiate are specified at run-time, for example, by dynamic loading; or
- to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or
- when instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state.
One important aspect of the prototype method is that an object should be able to close itself in order to facilitate copying of the object. So when a clone of an object is obtained, that object can be used to represent more objects of similar type. Thus, the user need not remember more object types and the Concrete classes are hidden from the user. Some of the advantages of Prototyping are,
- Adding and removing products at runtime - this allows the client to create any of the objects of the concrete classes at runtime by just using one prototype. Provides a much greater flexibility.
- Reduced Subclassing - This reduces the total number of subclasses that need to be created as the prototype method can simply call clone instead of asking a factory method to return an object. So, no creator class hierarchy is required, thus bettering Factory method in that aspect.
- applications can be configured dynamically.