Sunteți pe pagina 1din 28

Software Design Patterns

Software Design Patterns

Design patterns are recurring solutions to software design problems you find again and again in
real-world application development. Design patterns are about design and interaction of objects,
as well as providing a communication platform concerning elegant, reusable solutions to
commonly encountered programming challenges.

The Gang of Four (GOF) patterns are generally considered the foundation for all other patterns.
They are categorized in three groups: Creational, Structural, and Behavioral.

To give you a head start, the source code is provided in two forms: structural and real-world.
Structural code uses type names as defined in the pattern definition and UML diagrams. Real-
world code provides real-world programming situations where you may use the patterns.

Creational Patterns
Abstract Factory Creates an instance of several families of classes
Builder Separates object construction from its representation
Factory Method Creates an instance of several derived classes
Prototype A fully initialized instance to be copied or cloned
Singleton A class of which only a single instance can exist

Structural Patterns
Adapter Match interfaces of different classes
Bridge Separates an object’s interface from its implementation
Composite A tree structure of simple and composite objects
Decorator Add responsibilities to objects dynamically
Façade A single class that represents an entire subsystem
Flyweight A fine-grained instance used for efficient sharing
Proxy An object representing another object

Behavioral Patterns
Chain of Resp. A way of passing a request between a chain of objects
Command Encapsulate a command request as an object
Interpreter A way to include language elements in a program
Iterator Sequentially access the elements of a collection
Mediator Defines simplified communication between classes
Memento Capture and restore an object's internal state
Observer A way of notifying change to a number of classes
State Alter an object's behavior when its state changes
Strategy Encapsulates an algorithm inside a class
Template Method Defer the exact steps of an algorithm to a subclass
Visitor Defines a new operation to a class without change
What Are Design Patterns and Do I Need Them?

Software professionals may be familiar with the term "Design Patterns," but many have no idea of
where they come from and what they truly are. Consequently, some do not see the value and
benefits design patterns bring to the software development process, especially in the areas of
maintenance and code reuse. This article will bridge this gap by defining design patterns from a
historical perspective. It will also summarize the salient features of a typical design pattern and
arrive at a working definition so that you will know what they are and what to expect when you
incorporate them into your designs. Finally, it will explicitly summarize the benefits design
patterns bring to software development and why you should incorporate them into your work.
Subsequent articles will present more detailed descriptions of some of the more common design
patterns, and how they can be applied to software development on the .NET platform.

What Are Design Patterns and Where Do They Come From?


Design patterns are commonly defined as time-tested solutions to recurring design problems. The
term refers to both the description of a solution that you can read, and an instance of that solution
as used to solve a particular problem. (I like the analogy of comparing design patterns to a class
and an object instance of the class. Each is a different way to represent a thing.) Design patterns
have their roots in the work of Christopher Alexander, a civil engineer who wrote about his
experience in solving design issues as they related to buildings and towns. It occurred to
Alexander that certain design constructs, when used time and time again, lead to the desired
effect. He documented and published the wisdom and experience he gained so that others could
benefit. About 15 years ago, software professionals began to incorporate Alexander's principles
into the creation of early design pattern documentation as a guide to novice developers. This
early work led others to also write about design patterns and culminated in the publication of
Design Patterns: Elements of Reusable Object-Oriented Software in 1995 by Eric Gamma,
Richard Helm, Ralph Johnson, and John Vlissides. This book is considered to be the "coming
out" of design patterns to the software community at large and has been influential in the
evolution of design patterns since. Design Patterns described 23 patterns that were based on the
experience of the authors at that time. These patterns were selected because they represented
solutions to common problems in software development. Many more patterns have been
documented and cataloged since the publishing of Design Patterns. However, these 23 are
probably the best known and certainly the most popular.

Design patterns are represented as relationships between classes and objects with defined
responsibilities that act in concert to carry out the solution. To illustrate a design pattern, consider
the Adapter pattern, one of the original 23 patterns described in Design Patterns. Adapter
provides a solution to the scenario in which a client and server need to interact with one another,
but cannot because their interfaces are incompatible. To implement an Adapter, you create a
custom class that honors the interface provided by the server and defines the server operations in
terms the client expects. This is a much better solution than altering the client to match the
interface of the server.

The design pattern community is growing both in membership and coverage. The pattern
literature describes new patterns that solve emerging issues related to technical advancements.
As a software professional, you are the beneficiary of this body of knowledge. To use these
patterns, you will need to learn them and become familiar with them so you will know which
pattern to pull from your toolbox when a design issue arises. Many patterns have been
documented over the years. They have been classified in different ways by different authors.
Take the time to learn different ways to classify design patterns because you will gain greater
insight into them. As you learn more and more patterns, it would be a good idea to develop your
own classification system; one reflecting the way you utilize them.

Structure of a Design Pattern


Design pattern documentation is highly structured. The patterns are documented from a template
that identifies the information needed to understand the software problem and the solution in
terms of the relationships between the classes and objects necessary to implement the solution.
There is no uniform agreement within the design pattern community on how to describe a pattern
template. Different authors prefer different styles for their pattern templates. Some authors prefer
to be more expressive and less structured, while others prefer their pattern templates to be more
precise and high grain in structure. We will use the template first described by the authors of
Design Patterns to illustrate a template.

Term Description
Term Description
Pattern Name Describes the essence of the pattern in a short, but expressive, name
Intent Describes what the pattern does
Also Known List any synonyms for the pattern
As
Motivation Provides an example of a problem and how the pattern solves that problem
Applicability Lists the situations where the pattern is applicable
Structure Set of diagrams of the classes and objects that depict the pattern
Participants Describes the classes and objects that participate in the design pattern and their
responsibilities
Collaborations Describes how the participants collaborate to carry out their responsibilities
Consequences Describes the forces that exist with the pattern and the benefits, trade-offs, and
the variable that is isolated by the pattern

This template captures the essential information required to understand the essence of the
problem and the structure of the solution. Many pattern templates have less structure than this,
but basically cover the same content.

Benefits of Design Patterns


Design patterns have two major benefits. First, they provide you with a way to solve issues
related to software development using a proven solution. The solution facilitates the development
of highly cohesive modules with minimal coupling. They isolate the variability that may exist in the
system requirements, making the overall system easier to understand and maintain. Second,
design patterns make communication between designers more efficient. Software professionals
can immediately picture the high-level design in their heads when they refer the name of the
pattern used to solve a particular issue when discussing system design.

James Maioriello, Ph.D., is a Senior Design Engineer for Formation Systems Inc., a corporation
located in Massachusetts dedicated to the development of software systems for product
development in the processing industries. His position allows him to use his expertise and skills to
design solutions for chemists and other scientists so they can focus their talents on innovation.
Dr. Maioriello obtained degrees in Chemistry from Lehigh University and in Software Engineering
from Polytechnic University in New York.
Abstract Factory

The Abstract Factory is intended to provide a single interface for clients to use when they need to
create a family of related objects without having to specify concrete classes. For example,
imagine a system that needs to implement platform-specific user interface objects (menus,
toolbars, and so forth) for several different platforms. Abstract Factory simplifies the interface for
the client by localizing all of the initialization strategies within a single class, the Abstract Factory.
The pattern works to ensure that all the strategies can work together correctly.To understand
Abstract Factory, examine the class diagram shown in the following figure. Note that there are
two separate hierarchies. The first represents the various abstractions the client has interest in.
For each abstraction, there exists an abstract class definition (AbstractClass1, AbstractClass2,
and so on), and the subclasses that provide the concrete implementations (ConcreteClass1A,
ConcreteClass1B, and so forth). In the second hierarchy, an abstract AbstractFactory class is
defined to provide the interface for each class that is responsible for creating the members of a
particular family. For example, ConcreteFactoryB is responsible for creating objects from classes
ConcreteClass1B, ConcreteClass2B, and the like. All the client needs to worry about is which
family of objects it is interested in creating and calling the appropriate Factory method. Because
the client only knows about the abstract interface, it can interact with objects from different
families without having to know about their internal implementation details. This approach has the
benefit that the family can be allowed to grow without the need to modify the client. The primary
drawback of AbstractFactory is that it can limit your flexibility when you want to take advantage of
a specific capability a family may have. This is because you must provide a common interface
across all families, even if some of them do not have this capability.
Adapter

The Adapter is intended to provide a way for a client to use an object whose interface is different
from the one expected by the client, without having to modify either. This pattern is suitable for
solving issues that arise, for example, when: 1) you want to replace one class with another and
the interfaces do not match, and 2) you want to create a class that can interact with other classes
without knowing their interfaces at design time.

In the class diagram shown for Adapter in the following figure, the client wants to make a specific
request of an object (Adaptee). Ordinarily, this would be accomplished by creating an instance of
Adaptee and invoking SpecificRequest(...). In this case, however, the client cannot do so because
the interfaces do not match (the parameter types are different). Adapter helps you address this
incompatibility by providing classes that are responsible for redefining the data types on the
client's behalf. First, you create a custom Target class that defines methods using an interface
expected by the client. And second, you create a custom Adapter that implements the interface
expected by the Adaptee. The Adapter class subclasses the Target, and provides an alternate
implementation of the client requests in terms that the Adaptee expects. Adapter overrides the
Target method and provides the correct interface for Adaptee. This approach has the advantage
that it does not lead to modifications in the client. Adapter is a structural pattern and you can use
it to react to changes in class interfaces as your system evolves, or you can proactively use the
Adapter pattern to build systems that anticipate changing structural details.

Bridge

The Bridge is intended to decouple an abstraction from its implementation so both can vary
independently. For example, a significant maintenance headache entails the coupling that occurs
between custom classes and the class libraries they use. Bridges are useful for minimizing this
coupling by providing an abstract description of the class libraries to the client. For example, a
typical abstraction could be created for displaying a record set (DisplayRecordset as an
Abstraction). The DisplayRecordset class could then be implemented using a variety of user
interface elements (data grids, data lists, etc.), or with different formats (chart, table, and so forth).
This has the effect of keeping the client interface constant while you are permitted to vary how
abstractions are implemented. In addition, you can localize the logic used to determine which
implementation and which abstraction the client object should use. The remaining client methods
only need to know about the abstraction.

To illustrate the Bridge Pattern, consider the class diagram displayed in the following figure. The
client would have a method such as "SetupAbstraction(...)" that would be responsible for
determining which implementation to use for the abstraction. Then the client is free to interact
with the Abstraction and the proper implementation class will respond.

Builder

Builder is used to separate the construction of complex objects from their representation so the
construction process can be used to create different representations. The construction logic is
isolated from the actual steps used to create the complex object and, therefore, the construction
process can be reused to create different complex objects from the same set of simple objects.
This tends to reduce the size of classes by factoring out methods that are responsible for
constructing complex objects into a single class (called a Director) that is responsible for knowing
how to build complex objects. In addition, Builder can be used when you want to create objects in
a step-wise manner depending on parameters you acquire along the way.

The class diagram shown in the next figure shows the client instantiates the Builder and Director
classes. The Builder represents the complex object in terms of simpler objects and primitive
types. The client then passes the Builder object, as a parameter, to the Director's constructor,
which is responsible for calling the appropriate Builder methods. An abstract AbstractBuilder
class is created to provide the Director with a uniform interface for all concrete Builder classes.
Thus, you can add new types of complex objects by defining only the structure (Builder) and
reuse the logic for the actual construction process (Director). Only the client needs to know about
the new types. The Director simply needs to know which Builder methods to call.
Chain of Responsibility

One of the tenets of software engineering is to keep objects loosely coupled. The Chain of
Responsibility is intended to promote loose coupling between the sender of a request and its
receiver by giving more than one object an opportunity to handle the request. The receiving
objects are chained and pass the request along the chain until one of the objects handles it.

The class diagram, as illustrated in the following figure, shows that the client instantiates the
Request and passes it as a parameter to an instance of the Handler. Each concrete handler is
responsible for implementing an action to take on the Request object, and to maintain a reference
to the next handler in the chain. An abstract Handler is defined to provide a uniform interface for
the concrete handlers. The client is responsible for creating Request objects and then passing
them to the first handler in the chain. The chain (Handler1 -> Handler2 -> Handler3, and so forth)
can be set by the client by calling the SetSuccessor(...) for each handler it instantiates.
Command

The Command pattern is intended to encapsulate a request as an object. For example, consider
a window application that needs to make requests of objects responsible for the user interface.
The client responds to input from the user by creating a command object and the receiver. It then
passes this object to an Invoker object, which then takes the appropriate action. This allows the
client to make requests without having to know anything about what action will take place. In
addition, you can change that action without affecting the client. Practical uses for Command are
for creating queues and stacks for supporting "undo" operations, configuration changes, and so
on.

As shown in the following class diagram, the client instantiates the Receiver class and passes it
as an argument to the constructor of the ConcreteCommand class. The Receiver is responsible
for carrying out the specific actions required and fulfilling the request. An abstract class is defined
to provide a uniform interface for the concrete Command class. The concrete Command classes
are responsible for executing the appropriate methods on the Receiver. The client uses the
Invoker to ask the Command when to carry out the request. One drawback to the Command
pattern is that it tends to lead to a proliferation of small classes; however, it does lead to simpler
clients for supporting modern user interfaces.
Composite

The Composite is intended to allow you to compose tree structures to represent whole-part
hierarchies so that clients can treat individual objects and compositions of objects uniformly. We
often build tree structures where some nodes are containers of other nodes, and other nodes are
"leaves." Instead of creating separate client code to manage each type of node, Composite lets a
client work with either using the same code.

As the class following diagram shows, the composite pattern consists of an abstract class called
a Component, which provides interface for nodes and leaves alike. In addition, the Component
may also define a default implementation when it is appropriate. The Leaf and Composite classes
provide their own implementations, as well. The key to this pattern is the Composite class, which
uses a collection to create complex objects from other objects (Leaves or other Composites). In
addition, the composite object provides a way to iterate the composition to execute operations on
the individual objects in the composition.
Decorator

Ordinarily, an object inherits behavior from its subclasses. The decorator pattern is intended to
give you a way to extend the behavior of an object, and you can also dynamically compose an
object's behavior. Decorator allows you to do so without the need to create new subclasses.

When you examine the next class diagram, you will notice there are two basic types of classes.
The Component, whose behavior you want to extend, and the Decorator, which implements the
extended behavior. You can have any number of Components and Decorators. You also provide
abstract classes for both types of classes so the client has a common interface. The key to the
Decorator is the SetComponent(...) method, which takes a component as an argument. The client
is responsible for instantiating the Component and passing it to the Decorator's constructor. The
client is then free to utilize the Component's new methods, as implemented in the Director. A
really nice feature of the Decorator pattern is that you can decorate specific instances of objects.
This is difficult to do if you subclass your components directly.
Facade

With the many classes and subsystems we use, it is important to isolate our software and reduce
coupling. The Facade Pattern is intended to provide a unified interface to a set of interfaces in a
subsystem. The Facade defines a higher-level interface that makes the subsystems easier to
use. There are several benefits to Facade. First, it provides developers with a common interface
to the subsystem, leading to more uniform code. Second, it isolates your system and provides a
layer of protection from complexities in your subsystems as they evolve. Third, this protection
makes it easier to replace one subsystem with another because the dependencies are isolated.
The main disadvantage of Facade is that it tends to limit your flexibility. You are free, however, to
instantiate objects directly from the subsystems.

As the following class diagram shows, the facade pattern provides the client with a uniform
interface to the subsystem. Often the code that is inside a facade would have to be inside client
code without the facade. The subsystem code beneath the facade can change without affecting
the client code.
Factory Method

When developing classes, you always provide constructors for your clients' use. There are certain
circumstances in which you do not want your clients to know which class out of several to
instantiate. The Factory Method is intended to define an interface for clients to use to create an
object, but lets subclasses decide which class to instantiate. Factory Methods let a class defer
instantiation to subclasses.

As the next class diagram shows, the pattern uses two types of classes. The Product classes,
which are the classes that make up your application, and the Creator classes, which are
responsible for defining the Factory methods used to create instances of Product objects on the
clients behalf. The Creator class defines the factory method, which returns an object of type
Product. The patterns uses abstract Product and Creator classes to provide the client with a
uniform interface. Concrete classes provide the appropriate implementation for their respective
base class. In addition, Creator may also define a default implementation of the factory method
that returns a default ConcreteProduct object. It may also call the factory method to create a
Product object. Any of these approaches can be used to balance the forces at work on your
project.
Flyweight

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).
Interpreter

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.
Iterator

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.
Mediator

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.
Memento

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(...).
Observer

The Observer pattern is useful when you need to present data in several different forms at once.
The Observer is intended to provide you with a means to define a one-to-many dependency
between objects so that when one object changes state, all its dependents are notified and
updated automatically. The object containing the data is separated from the objects that display
the data and the display objects observe changes in that data.

As the next class diagram shows, there are basically two different types of objects: a Subject and
an Observer. The Subject corresponds to the data object whose state you want to track. The
Observer is the object that is interested in changes in the Subject object data. To set up the
pattern, Subject classes implement a method for registering Observers and for attaching and
detaching them from a collection object. In addition, Subjects also implement GetState(...) and
SetState(...) methods for the Observer to call. Finally, you also need a notify(...) method to notify
all registered Observers when the data has changed. In addition to the Subject, you can also
create Subject subclasses to store the Subject's state to satisfy any special requirements of
concrete Observers. For Observers, you define a custom abstract Observer class to provide
clients with a uniform interface, and subclass the implementation details.
Prototype

The Prototype is a simple pattern intended to provide you with a way to dynamically select which
object to instantiate, and to return a clone of a prototypical object. This pattern is useful in
applications that use a graphical toolbox to create instances of subclasses that represent the
objects used by an application.

As the following class diagram shows, the pattern consists of an abstract Prototype class that
defines a Clone(...) method that all concrete Prototypes implement. The client simply calls the
Clone(...) method for the specific concrete Prototype it requires.
Proxy

The Proxy is intended to provide you with a way to use a surrogate or placeholder to another
object in order to control access to it. This pattern is useful for situations where object creation is
a time consuming process and can make an application appear sluggish. A proxy object can
provide the client with feedback while the object is being created. Proxy can also be used to
provide a more sophisticated reference to an object. For example, the proxy object can
implement additional logic on the objects behalf (security, remote procedure calls, an so forth).

The next class diagram shows the pattern consists of two types of classes. The Subject is the
actual object the client is interested in. An abstract Subject class is defined to provide both the
Proxy and the client with a uniform interface to the Actual Subject classes. The Request(...)
method is responsible for creating and returning an instance of the Subject. The Proxy overrides
this method with its own implementation and manages the reference to the ActualObject class.
The Proxy can also implement its own functionality when instantiating the ActualObject class. The
client is responsible for selecting and instantiating the Proxy, and some time later, making a call
to the Request(...) method. The Proxy responds by returning the actual object.

Singleton

There are a number of situations in programming where you need to assure that you have only
one instance of a class. Print spoolers, window managers, and so forth, are examples of
Singletons. The Singleton is intended to provide a way to ensure that a class provides one
instance of itself, and to provide a global point of access.

The following class diagram shows the Singleton is a simple pattern. The Singleton class is
responsible for instantiating itself and passing that instance on to the client. Therefore, the access
modifier for the Singleton constructor must be either private or protected so the client cannot call
the constructor itself. The Singleton also provides a method that returns the sole instance of itself
to the client. The first time the client requests an instance, the Singleton will create that instance
internally and store it as a private member. Each subsequent call from the client will return that
instance.
State

The State pattern is useful when you want to have an object represent the state of an application,
and you want to change the state by changing that object. The State pattern is intended to
provide a mechanism to allow an object to alter its behavior in response to internal state changes.
To the client, it appears as though the object has changed its class. The benefit of the State
pattern is that state-specific logic is localized in classes that represent that state.

The next class diagram shows how to implement the State pattern. You define a Context class
and an abstract class, State class, which your custom State handlers can implement. The
Context class is responsible for calling the appropriate state handlers. The client instantiates the
Context class and calls the Request(...) method with the appropriate arguments. The Request(...)
method then determines which state handler should handle the request and passes the data to
the Handle(...) method. The benefit of the State pattern is deeply nested or complex if statements
are localized in the Context class instead of the client. Therefore, when the state of an application
has to change in response to use input, the client merely passes this data to the Context object
and the appropriate updates to the application state are made.

Strategy

The Strategy pattern is very useful for situations where you would like to dynamically swap the
algorithms used in an application. If you think of an algorithm as a strategy for accomplishing
some task, you can begin to imagine ways to use Strategy. Strategy is intended to provide you
with a means to define a family of algorithms, encapsulate each one as an object, and make them
interchangeable. Strategy lets the algorithms vary independently from clients that use them.

The following class diagram shows the Strategy pattern basically consists of a context class and
a set of strategy classes. You define an abstract strategy class that your custom strategy classes
can implement. Each strategy object implements the algorithm that makes it unique. The client
instantiates the strategy class and passes it as an argument when it calls the constructor of the
context class. The client is then able to call methods on the context object and any strategy
specific methods as necessary. Because the context creates an instance of the abstract Strategy,
the client is effectively using polymorphism to call strategy-specific methods on the context object.

Template Method

The Template Method is a simple pattern. You have an abstract class that is the base class of a
hierarchy, and the behavior that is common to all objects in the hierarchy is implemented in the
abstract class. Other details are left to the individual subclasses. The Template pattern is
basically a formalism of the idea of defining an algorithm in a class but leaving some of the details
to be implemented in subclasses. Another way to think of the Template Method is that it allows
you to define a skeleton of an algorithm in an operation and defer some of the steps to
subclasses. Template Method lets subclasses redefine certain steps of an algorithm without
changing the algorithm's structure.

The next class diagram shows the Template method simply consists of an abstract class and
subclasses. These classes define a set of primitive behaviors that make up a specific piece of
functionality in the application. Behaviors common to all objects (TemplateMethod(..)) are
implemented in the abstract class. Subclasses are free to provide an alternate implementation
(overriding a method), or to provide methods specific to a particular concrete class (such as
Step1(...) and Step2(...)).
Visitor

The Visitor pattern uses an external class to act on data in other classes. This is a useful
approach to you when you have a polymorphic operation that cannot reside in the class
hierarchy. Visitor is also a useful way to extend the behavior of a class hierarchy without the need
to alter existing classes or to implement the new behavior in every subclass that requires it.

The following class diagram shows that the Visitor pattern consists of two different types of
classes, Elements and Visitors. Elements are the objects your application uses and Visitors are
encapsulated behaviors that are required for each concrete Element. Each Element must
implement the Accept( ) method, which takes Visitor as an argument. In addition, each Visitor
must provide an implementation of the VisitElementX( ) method for each element in the object
structure. The client acts to instantiate the element class and the visitor class. The client then
passes the visitor object to the element's Accept( ) method, and then calls the elements methods
to have it perform operations. The actual action that gets performed is determined by which visitor
class was instantiated by the client.
Using Design Patterns in UML

Many a time when you design or implement solutions for a software system, you get the feeling of
dij` vu. Even though this may sound too extreme, it is undeniable that parts of different software
systems may share similar aspects. With software design going the assembly line way, reusability
has become an important criterion in software design

A pattern is a commonly occurring reusable piece in software system that provides a certain set
of functionality. The identification of a pattern is also based on the context in which it is used. So,
using patterns in modeling of systems helps in keeping design standardized and more
importantly, minimizes the reinventing of the wheel in the system design. This article is all about
patterns; especially design patterns.

Ok so you might ask how does a pattern relate to the UML? The patterns that we encounter need
to be captured and documented in a sufficiently descriptive manner so that they can be referred
for future use. UML provides the perfect tools to do just this. The class diagram in UML can be
used to capture the patterns identified in a system. In addition, UML has a sufficiently extensive
and expressive vocabulary to capture the details of patterns.

Over the previous articles in this series, we explored the different UML diagrams and learned how
to model each of the diagrams in a case study application. Building on this background we will
see how to leverage the usefulness of well-known patterns to make application designing a lot
easier.
To sum up, a pattern should have the following characteristics

• Useful solution
• Reusable
• Contextual

Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides first popularized the concept of
patterns with their book on design patterns. These authors came to be known as the "Gang Of
Four" or GOF. The name stuck ever since and the patterns catalog that they documented is
known as the GOF patterns catalog.

Design Patterns

Since a system is made up of static as well as dynamic elements, you will find patterns that can
be used for either of these types. For static elements of a system especially the architecture and
design of a system, there are design patterns - the focus of this article. The dynamic aspects of a
system are abstracted and captured as process patterns. But patterns are not limited to this.
Patterns can be abstracted for the implementation aspects of a system as well.

Based on how they are to be used, patterns are primarily categorized as:

• Creational
• Structural
• Behavioral

Creational

Creational patterns define mechanisms for instantiating objects. The implementation of the
creational pattern is responsible for managing the lifecycle of the instantiated object. A few
examples of Creational design patterns are listed below.

Factory

One of the easily recognized and frequently used design patterns is the Factory pattern. If you
have designed any object that is responsible for creating and maintaining the lifecycle of another
object, you have used the Factory pattern. Obtaining a database connection in your application
using a connection manager or connection factory object is a good example of the Factory
pattern.

Figure 1. Factory pattern

Singleton

A singleton is another example of a factory pattern. What makes the Singleton pattern unique is
that one and only one instance of the object can exist irrespective of the number of times the
object is instantiated. The most common use of a Singleton pattern is for server applications like
a Java based Remote Method Invocation (RMI) server application.

Figure 2. Singleton pattern

Structural

The composition of objects and their organization to obtain new and varied functionality is the
underlying basis of Structural patterns. A few examples of Structural design patterns are listed
below.

Adapter

In the Adapter pattern, an object provides an implementation of an interface used by other objects
in a consistent way. The adapter object wraps different disparate implementations of the interface
and presents a unified interface for other objects to access. A good example of this is a database
driver like an ODBC (Open Database Connectivity) or JDBC (Java Database Connectivity) driver
that wraps the custom database accessing implementation for different databases and yet,
presents a consistent interface that is a published and standardized API.

Figure 3. Adapter pattern

Proxy

A Proxy pattern constitutes use of proxy objects during object interaction. A proxy object acts as a
substitute for the actual object. Use of proxy objects is prevalent in remote object interaction
protocols. As an example, when an object needs to interact with a remote object, say across a
network, the most preferred way of encapsulating and hiding the interaction mechanism is by
using a proxy object that mediates communication between the requesting object and the remote
object.

Figure 4. Proxy pattern


Behavioral

Interaction between different objects is specifically covered by Behavioral patterns. Some


examples of behavioral patterns are given below.

Command

The Command pattern is commonly used for gathering requests from client objects and
packaging them into a single object for processing. The Command pattern allows for having well
defined command interfaces that are implemented by the object that provides the processing for
the client requests packaged as commands.

Figure 5. Command pattern

Iterator

A simple mechanism to traverse and access a list of objects is defined by the Iterator pattern. The
Iterator pattern encapsulates the internal implementation of the list while providing a standardized
mechanism for list traversal.

Figure 6. Iterator pattern

S-ar putea să vă placă și