Software Design Patterns

advertisement
Software Design Patterns
Gang of Four (GoF, made up of Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides
1
What is Design Pattern?
 Design patterns are “descriptions of communicating objects
and classes that are customized to solve a general design
problem in a particular context.”
—GANG
OF FOUR
 Design patterns offer solutions to common application design
problems.
 In object-oriented programming, design patterns are
normally targeted at solving the problems associated with
object creation and interaction, rather than the large-scale
problems faced by the overall software architecture.
 They provide generalized solutions in the form of boilerplates
that can be applied to real-life problems.
Design Patterns are visualized using a class diagram
 They show the behaviors and relations between classes
 Diagram shows the inheritance relationship between three classes.
 The subclasses CheckingAccount and SavingsAccount inherit from their
abstract parent class BankAccount.
public class SingleObject {
//create an object of SingleObject
private static SingleObject instance = new
SingleObject();
//make the constructor private so that this class
cannot be instantiated
private SingleObject(){}
//Get the only object available
public static SingleObject getInstance()
{ return instance; }
public void showMessage()
{ System.out.println("Hello World!"); }
}
public class SingletonPatternDemo {
public static void main(String[] args) {
//illegal construct
//Compile Time Error: The constructor SingleObject() is not visible
//SingleObject object = new SingleObject();
//Get the only object available
SingleObject object = SingleObject.getInstance();
//show the message
object.showMessage(); } }
Why Design Patterns?
 Simplifies object identification
Simplifies system decomposition
 Proven & tested technique for problem solving
 Improves speed & quality of
design / implementation
 Can be adapted / refined for specific system
under construction
6
Design Patterns Classification
Two categories
Class Scope:
Relationship between
classes & subclasses
statically defined at
run-time
Object Scope:
Object relationships
(what type?)
Can be manipulated at
runtime (so what?)
7
Common Aspects of Object-Oriented
Languages
Design pattern models are based and depend
highly on the aspects of object-oriented
languages.
Patterns-based programming doesn’t make much
sense outside such languages.
 Aspects of object-oriented languages like
encapsulation, polymorphism, abstraction, and
inheritance all extend their properties into
patterns-based coding.
Patterns methodology is an extension of objectoriented methodology.
Encapsulation
Encapsulation is one of the most important aspects
of object-oriented languages.
The rule of encapsulation is
 one of keeping things private
masked inside the domain of an object, package,
namespace, class, or interface
The rule of encapsulation allows only expected
access to pieces of functionality.
The rule of encapsulation is used in almost every
aspect of OOP
This rule allows us to build patterns like facades, proxies,
bridges, and adapters.
Encapsulation
Encapsulation allows us to hide with an interface
or class structure some functionality
we do not wish to be publicly or globally known.
 Encapsulation allows us to define scope inside
our programs, and helps us to define and group
modules of logic.
Encapsulation provides ways to allow objects to
communicate without that communication getting
either too complex
Encapsulation provides rules of engagement
between different code bases
It helps us decide what functionality can be known and
what needs to be hidden.
Encapsulation Example
class CustomerPayment
{
public double PostPayment (int loanId, double payment)
{
.....performs post of payment to customer account
}
public ArrayList GetAmortizedSchedule(int loanId)
{
...returns an amortization schedule in array
}
}
Encapsulation Example
 The two methods in the code are visible as public.
 We can assume that the CustomerPayment class has many
other methods and code to help perform some of the
functions of its two public methods
 we cannot access them outside the class code
 they are in effect invisible to any other classes in the code
domain.
 This gives us proper encapsulation of the class methods
 Only the required methods to be accessed are allowed .
 The method of encapsulation for this class
 PostPayment()
 GetAmortizedSchedule()
 Thet can be accessed outside the class.
Polymorphism
Polymorphism is another important aspect of
object-oriented programming.
 The rule of polymorphism states that classes can
be altered according to their state
 in effect making them a different object based on
values of attributes, type, or function.
 We use polymorphism in almost every coding
situation, especially patterns.
This rule gives us the power to use abstraction
and inheritance,
 It allows inherited members to change according to
how they implement their base class.
Polymorphism
 It also allows us to change a class’s purpose and
function based on its current state.
 Polymorphism helps interfaces change their
implementation class type simply by allowing
several different classes to use the same
interface.
Polymorphic declarations of specific
implementations of classes with a common base
or interface are extremely common in objectoriented code
Polymorphism Example
if(IsAntiqueAuto)
AntiqueAuto auto = new AntiqueAuto();
int cylinders = auto.Cylinders();
int numDoors = auto.NumberDoors();
int year = auto.Year();
string make = auto.Make();
string model = auto.Model();
}
Polymorphism Example
The first thing you need to answer is why do we
want to change this code?
The answer might be one of portability: You wish
to have many car types and pass the logic of
creating them via the class itself, rather than via a
logical Boolean statement.
Or you might wish to make sure all auto classes
have the same methods so your code accessing
the object always knows what to expect.
Polymorphism Example
In the code example below we can see an
interface, IAuto, and below it some classes
that we have modified to implement this
interface.

Polymorphism Example
public interface IAuto
{
int Cylinders();
int NumberDoors();
int Year();
string Make();
string Model();
}
class AntiqueAuto : IAuto....
class SportsCar : IAuto....
class Sedan : IAuto.....
class SUV : IAuto.....
Each class also implements the
interface’s method,
Each functioning in its own way,
returning values according to the logic
specific to the implemented methods
on each class.
class AntiqueAuto : IAuto
{
public int Cylinders()
{
return 4;
}
public int NumberDoors()
{
return 3;
}
public int Year()
{
return 1905;
}
public string Make()
{
return "Ford";
}
public string Model()
{
return "Model T";
}
}
 we instantiate the AntiqueAuto class as an instance
of the IAuto interface and call each of the interface
methods.
Each method returns a value from the methods
implemented on the AntiqueAuto class
IAuto auto = new AntiqueAuto();
int cylinders = auto.Cylinders();
int numDoors = auto.NumberDoors();
int year = auto.Year();
string make = auto.Make();
string model = auto.Model();
Polymorphism Example
If we changed the implemented class to SportsCar or
another auto type, the methods would return
different values.
 This is how polymorphism comes into play in class
relationships.
By changing the class type for a common interface or
abstraction, we can change the functionality and
scope of the code without having to code
if...then...else statements to accomplish the same
thing.
Inheritance and Abstraction
Inheritance and abstraction are also very
important features of object-oriented
languages.
 They provide a way to make polymorphic
representations of objects and object
relationships that can be managed at run time
or compile time.
.
Inheritance
Inheritance is the ability of one object to be
derived by creating a new class instance from
a parent or base class
 overloading the constructor(s), methods,
attributes of that parent object
implementing them in the instance.
Inheritance is important
Many times an object contains some base
functionality that another object also needs
Instead of maintaining the same logic in two
objects
They can share and even override or change this
functionality by using a base or parent class.
Inheritance
The base or parent object should be defined
in such a way that several common derived
objects can use the same common
functionality from the parent.
The parent should only contain functionality
common to all its children
Abstraction
 Abstraction is the actual method in which we use
inheritance.
 Abstraction is the ability to abstract into a base class
some common functionality or design that is common
to several implementation or instanced classes.
 The difference between implementation and abstract
classes is
Abstractions of classes cannot be instanced,
Implementations can be instanced
 Abstraction and inheritance are both aspects of
polymorphism
 The reverse is also true
Collections of Objects
Another important aspect of object-oriented
languages is how they deal with collections of
objects.
 The equals implementation for objects is an
important aspect of dealing with objects
inside a collection.
Languages like C#, VB.NET, and Java all use this
method to help index and compare objects in
collections.
Collections of Objects
When a hash table or other collection object
indexes and compares an object, it uses the
GetHashCode() method
This method can be overridden to capture a
more accurate sampling of the intrinsic
properties or state of the object.
 GetHashCode() method can return an integer
representation of the concatenated state of
the properties within an object.
Collections of Objects
The default implementation of GetHashCode() in
objects that contain state variables or values is not
guaranteed to be unique.
To provide a complete representation of state, the
values of each variable that represents the object’s
state need to be part of the hashing algorithm.
public override int GetHashCode()
{
return _name.GetHashCode() ^
_address.GetHashCode();
}
Collections of Objects
The proper operational sequence usually starts with a null
check, then a class type comparison, and then a
comparison of all the value types (or reference types)
that influence the state of the class:
public override bool Equals(object obj)
{
if(obj != null && obj is Component)
return _name.Equals(((Component)obj).Name) &&
_address.Equals(((Component)obj).Address);
else
return false;
}
Collections of Objects
There are some basic rules when testing the equals implementation
for proper return values:
 obj1.Equals(obj1) = true — an object always equals itself.
 obj1.Equals(obj2) = obj2.Equals(obj1) — equals implementations
across different class instances always return true on both classes if
equal.
 obj1.Equals(obj2) && obj2.Equals(obj3) && obj3.Equals(obj1) =
true — if object 1 is equal to object 2 and object 2 is equal to object
3, then object 3 must be equal to object 1.
 All calls to Equals() return the same value unless the class’s state or
internal value is modified.
 Equals(null) always returns false.
THE CONCEPT OF PATTERNS
Construction Architecture Patterns
The first idea of using patterns was for building
and proposed by the architect Christopher
Alexander.
He found recurring themes in architecture, and captured
them into descriptions
He called them patterns.
The term 'pattern' appeals to the replicated
similarity in a design
The similarity makes room for variability and customization
in each of the elements
Alexander defines: «Each pattern is a three part rule
which express a relation between a certain context, a
problem and a solution.
Each pattern is a relationship between
a certain context,
a certain system of forces which occurs
repeatedly in that context
a certain spatial configuration which allows these
forces to resolve themselves.
A pattern is an instruction and shows how this
configuration can be used over and over again.
The pattern is a thing that happens in the world
The rule which tell us how to create that thing and
when we must create it.
KitchenViewer Interface:
An architectural pattern example
Wall
cabinet
menu
Counter
display area
styles
Floor
cabinet
Modern
Classic
36
Antique
Arts & Crafts
KitchenViewer Example
Modern
Classic
Antique
37
Arts & Crafts
Selecting Antique Style
Modern
Classic
Antique
38
Arts & Crafts
Specific Design Purposes for
KitcherViewer
The procedure of rendering the various styles is
basically the same regardless of the style .
The code is as follows:
Counter counter =new Counter();
draw (counters);
A single block of code that executes in several possible
ways, depending on the context  Polymorphism
An application must construct a family of objects at runtime.
The design must enable choice among several families of
styles
An Introduction to Design Pattens
Example Application: Without applying a
Design Pattern
renderKitchen() method is used.
This code would have to be repeated for every style
The code that is supposed to be duplicated becomes
different in different places.
Example Application: Applying a Design Pattern
renderKitchen(myStyle) method is used
KitchenViewer design purpose is implemented by
applying Abstract Factory design pattern.
KitchenViewer Without Design Patterns
Client
renderKitchen()
Kitchen
WallCabinet
ModernWallCabinet
FloorCabinet
AntiqueWallCabinet
ModernFloorCabinet
41
AntiqueFloorCabinet
Without Applying Design Patterns
renderKitchen() method have to be repeated
for every style
The method results in more prone-error and far
less maintainable code
The code that is supposed to be duplicated
becomes different in different places.
The result is repetitive and complicated .
It is inflexible, hard to prove correct, and hard
to reuse
Applying Abstract Factory Design Pattern
The object will have responsibility for creating the
kitchen .
Instead of creating the object directly (for example
AntiqueWallCabinet objects), a parameterized
version is used for renderKitchen()
At run time, the class of myStyle determines the
version of getWallCabinet()executed.
The KitchenStyle method is introduced and called
 This class has subclasses , and each support separate
implementations of getWallCabinet() and getFloorCabinet()
The Abstract Factory Idea
KitchenStyle
WallCabinet
getWallCabinet()
getFloorCabinet()
…
FloorCabinet
AntiqueWallCabinet
ModernKStyle
AntiqueKStyle
getWallCabinet()
getFloorCabinet()
getWallCabinet()
getFloorCabinet()
FloorCabinet getFloorCabinet()
{ return new ModernFloorCabinet(); }
…
AntiqueFloorCabinet
FloorCabinet getFloorCabinet()
{ return new AntiqueFloorCabinet(); }
44
Processing the Abstract Factory Pattern:
KitchenViewer
KitchenViewer design purpose is implemented by
applying Abstract Factory design pattern.
AntiqueWallCabinet objects are not created
directly.
A parameterized version of renderKitchen() delegates
their creation such as the following:
new AntiqueWallCabinet();//applies only to antique
style.
myStyle.getWallCabinet(); //applies to the style chosen
at run time.
.
Processing the Abstract Factory Pattern:
KitchenViewer
At run time , the class of myStyle determines the
version of getWallCabinet() and produces the
appropriate kind of wall cabinet
Processing the Abstract Factory Pattern:
KitchenViewer
To carry out this process, a new class
KitchenStyle is introduced.
KitchenStyle supports the methods
getWallCabinet(), getFloorCabinet() and so
on.
KitchenStyle have subclasses ModernStyle,
AntiqueStyle.
Due to the polymorphism, executing
myStyle.getFloorCabinet()
has differently effects when myStyle is an object
of ModernKStyle versus an object of
AntiqueKStyle.
Client code references Kitchen,
KitchenStyle,WallCabinet and FloorCabinet, but
does not appear in the client code.
Abstract Factory Design Pattern Applied to
Client
KitchenViewer
renderKitchen( KitchenStyle )
KitchenStyle
getWallCabinet()
getFloorCabinet()
Kitchen
getWallCabinet()
getFloorcabinet()
WallCabinet
FloorCabinet
ModernWallCabinet
ModernKStyle
getWallCabinet()
getFloorCabinet()
AntiqueKStyle
getWallCabinet()
getFloorCabinet()
AntiqueWallCabinet
ModernFloorCabinet
AntiqueFloorCabinet
49
Abstract Factory Design Pattern
Properties
Provide an interface for creating families of related or
dependent objects without specifying their concrete
classes.
 A hierarchy that encapsulates: many possible
platforms, and the construction of a suite of products.
 The new operator considered harmful
Problem
If an application is to be portable, it needs to
encapsulate platform dependencies.
These platforms might include: windowing system,
operating system, database…
General
Structure:
Abstract
Factory
Pattern
 The Abstract Factory defines a Factory Method per product.
 Each Factory Method encapsulates the new operator and
the concrete, platform-specific, product classes.
 Each platform is then modeled with a Factory derived class.
The Factory Method Pattern
Product
Defines the interface for the type of objects the factory
method creates
ConcreteProduct
Implements the Product interface
Creator
Declares the factory method, which returns an object of
type Product
ConcreteCreator
Overrides the factory method to return an instance of a
ConcreteProduct
Creator relies on its subclasses to implement the factory
method so that it returns an instance of the appropriate
ConcreteProduct
The Factory Method Pattern
 Code is made more flexible and reusable by the elimination of
instantiation of application-specific classes
 Code deals only with the interface of the Product class and
can work with any ConcreteProduct class that supports this
interface
 Clients might have to subclass the Creator class just to
instantiate a particular ConcreteProduct
Creator can be abstract or concrete
Should the factory method be able to create
multiple kinds of products?
If so, then the factory method has a
parameter (possibly used in an if-else!) to decide
what object to create
Factory Design Pattern
 Online bookstores that can choose different book distributors
to ship the books to the customers
 Both BookStoreA and BookStoreB choose which distributor
(EastCoastDistributor or MidWestDistributor or
WestCoastDistributor) to use based on the location of the
customer.
 This logic is in each bookstore's GetDistributor method.
Abstract Factory Pattern
The abstract factory design pattern is an
extension of the factory method pattern,
The abstract factory pattern allows to create
objects without being concerned about the
actual class of the objects being produced.
The abstract factory pattern extends the
factory method pattern by allowing more
types of objects to be produced.
Extension of GetDistributor() Method
to Abstract Factory Pattern
We can extend GetDistributor() method by
Adding another product that the factories can
produce.
In this example, we will add Advertisers that help the
bookstores advertise their stores online.
Each bookstore can then choose their own
distributors and advertisers inside their own
GetDistributor and GetAdvertiser method.
public void Advertise(IBookStore s)
{
IAdverister a = s.GetAdvertiser();
a.Advertise();
}
This allows to have client code (calling code) such as:
public void Advertise(IBookStore s)
{
IAdverister a = s.GetAdvertiser();
a.Advertise();
}
 Regardless if you pass in BookStoreA or BookStoreB
into the method, this client code does not need to be
changed since it will get the correct advertiser
automatically using the internal logics within the
factories.
 It is the factories (BookStoreA and BookStoreB) that
determines which advertiser to produce.
 The same goes for choosing which book distributor to
produce
Abstract Factory Design Pattern
The Benefit of the Abstract Factory Pattern
 The benefit of the Abstract Factory pattern is that it
allows you to create a groups of products (the
distributors and the advertisers) without having to
know the actual class of the product being produced.
 The result is that you can have client code that does
not need to be changed when the internal logic of the
factories changed.
 We can change the types of the products (the
distributors and the advertisers) by changing the code
in the factories (the bookstores) without changing the
client code
public enum CustomerLocation { EastCoast, WestCoast }
class Program
{
static void Main(string[] args)
{
IBookStore storeA = new
BookStoreA(CustomerLocation.EastCoast);
Console.WriteLine("Book Store A with a customer from East Coast:");
ShipBook(storeA);
Advertise(storeA);
IBookStore storeB = new
BookStoreB(CustomerLocation.WestCoast);
Console.WriteLine("Book Store B with a customer from West Coast:");
ShipBook(storeB);
Advertise(storeB);
}
//**** client code that does not need to be changed ***
private static void ShipBook(IBookStore s)
{ IDistributor d = s.GetDistributor();
d.ShipBook();
}
//**** client code that does not need to be changed ***
private static void Advertise(IBookStore s)
{ IAdvertiser a = s.GetAdvertiser();
a.Advertise();
}
//the factory
public interface IBookStore
{
IDistributor GetDistributor();
IAdvertiser GetAdvertiser();
}
//concrete factory
public class BookStoreA : IBookStore
{
private CustomerLocation location;
public BookStoreA(CustomerLocation location)
{
this.location = location;
}
IDistributor IBookStore.GetDistributor()
{
//internal logic on which distributor to return /
/*** logic can be changed without changing the client code ****
switch (location)
{
case CustomerLocation.EastCoast:
return new EastCoastDistributor();
case CustomerLocation.WestCoast:
return new WestCoastDistributor();
}
return null;
}
IAdvertiser IBookStore.GetAdvertiser()
{
//internal logic on which distributor to return
//*** logic can be changed without changing the client code ****
switch (location)
{
case CustomerLocation.EastCoast:
return new RedAdvertiser();
case CustomerLocation.WestCoast:
return new BlueAdvertiser();
}
return null;
}
} //end of factory class
public class BookStoreB : IBookStore
//concrete factory
{ private CustomerLocation location;
public BookStoreB(CustomerLocation location)
{
this.location = location; }
IDistributor IBookStore.GetDistributor()
{ switch (location)
{ case CustomerLocation.EastCoast:
return new EastCoastDistributor();
case CustomerLocation.WestCoast:
return new WestCoastDistributor();
} return null; }
IAdvertiser IBookStore.GetAdvertiser()
{switch (location)
{ case CustomerLocation.EastCoast:
return new BlueAdvertiser();
case CustomerLocation.WestCoast:
return new RedAdvertiser();
}
return null; }
}
//the product
public interface IDistributor
{
void ShipBook();
}
//concrete product
public class EastCoastDistributor : Idistributor
{
void IDistributor.ShipBook()
{
Console.WriteLine("Book shipped by East Coast Distributor"); } }
//concrete product
public class WestCoastDistributor : IDistributor
{
void IDistributor.ShipBook()
{
Console.WriteLine("Book shipped by West Coast Distributor"); } }
public interface IAdvertiser //the product
{
void Advertise();
}
public class RedAdvertiser : IAdvertiser //concrete product
{
void IAdvertiser.Advertise()
{ Console.WriteLine("Advertised by RedAdvertiser"); } }
public class BlueAdvertiser : IAdvertiser //concrete product
{
void IAdvertiser.Advertise()
{ Console.WriteLine("Advertised by BlueAdvertiser");
}}
Structural Design Patterns
In software engineering, structural design
patterns are design patterns that ease the design
by identifying a simple way to realize
relationships between entities
The adapter pattern is a design pattern that is
used to allow two incompatible types to
communicate.
 Where one class relies upon a specific interface
that is not implemented by another class, the
adapter acts as a translator between the two
types.
Adapter pattern
Adapter pattern is structural pattern which
defines a manner for creating relationships
between objects.
This pattern translates one interface for a class
into another compatible interface.
Adapter pattern is newer used when creating a
new system.
It is usually implemented when requirements are
changed and we must implement some
functionality of classes which interfaces are not
compatible with ours.
Adapter Design Pattern
Client: represents the class which need to use an incompatible interface.
This incompatible interface is implemented by Adaptee.
ITarget: defines a domain-specific interface that client uses. In this case
it is an simple interface, but in some situations it could be an abstract
class which adapter inherits. In this case methods of this abstract class
must be overriden by concrete adapter.
Adaptee: represents a class provides a functionality that is required by
client.
Adapter: is concrete implementation of adapter. This class translates
incompatible interface of Adaptee into interface of Client.
static class Program
{
static void Main()
{
var client = new Client(new Adapter());
client.Request(); }
public interface ITarget
{
void MethodA();
}
public class Client
{
private readonly ITarget _target;
public Client(ITarget target)
{ _target = target; }
public void Request()
public class Adaptee
{ public void MethodB()
{
Console.WriteLine("Adaptee's MethodB called");
}}
public class Adapter : ITarget
{
readonly Adaptee _adaptee = new Adaptee();
public void MethodA()
{ _adaptee.MethodB();
}
}
}
Bridge Pattern
Simple inheritance cannot meet the immediate
needs where abstraction is not desired.
When abstraction or inheritance is used, you are
tied to the exact definition of that abstraction.
Some cases would require classes not to be
inherited
instead we would like to adapt other classes to act
as the desired type without modifying either class.
The adapter houses an
instance variable of the
desired type to adapt as
a private instance
variable.
 This instance variable
is not changeable in the
class.
This means it is not set
as an abstract or base
variable but as a
concrete type.
We hide this instance
variable’s methods,
properties, and events
behind overridden
methods, properties
Behavioral Patterns
Behavioral patterns are patterns whose purpose
is to facilitate the work of algorithmic calculations
and communication between classes.
They use inheritance to control code flow.
They define and produce process and run-time
flow and identify hierarchies of classes and when
and where they become instantiated in code.
Some define class instance
Some hand off work from one class to another, and
 Some provide placeholders for other functionality.
Command Pattern
An object that contains a symbol, name or key
that represents a list of commands, actions or
keystrokes.
This is the definition of a macro
The Macro represents a command that is built
from the reunion of a set of other commands, in
a given order.
 Just as a macro, the Command design pattern
encapsulates commands (method calls) in objects
allowing us to issue requests without knowing
the requested operation or the requesting object.
Command Pattern
The Command pattern has three main components:
The invoker component acts as a link between the
commands and the receiver
houses the receiver and the
individual commands as they are sent.
The command is an object that encapsulates a request
to the receiver.
The receiver is the component that is acted upon by
each request.
Command Pattern
Command design pattern provides the options
to queue commands,
undo/redo actions
other manipulations.
Intent
 encapsulate a request in an object
 allows the parameterization of clients with different
requests
 allows saving the requests in a queue
Placing Orders for Buying and Selling
Stocks
public interface Order {
public abstract void execute ( );
}
// Receiver class.
class StockTrade {
public void buy() {
System.out.println("You want to buy stocks");
}
public void sell() {
System.out.println("You want to sell stocks ");
}
}
// Invoker.
class Agent {
private m_ordersQueue = new ArrayList();
public Agent() {
}
void placeOrder(Order order) {
ordersQueue.addLast(order);
order.execute(ordersQueue.getFirstAndRemove());
}
}
//ConcreteCommand Class.
class BuyStockOrder implements Order {
private StockTrade stock;
public BuyStockOrder ( StockTrade st) {
stock = st;
}
public void execute( ) {
stock . buy( );
}
}
//ConcreteCommand Class.
class SellStockOrder implements Order {
private StockTrade stock;
public SellStockOrder ( StockTrade st) {
stock = st;
}
public void execute( ) {
stock . sell( );
}
}
// Client
public class Client {
public static void main(String[] args) {
StockTrade stock = new StockTrade();
BuyStockOrder bsc = new BuyStockOrder (stock);
SellStockOrder ssc = new SellStockOrder (stock);
Agent agent = new Agent();
agent.placeOrder(bsc); // Buy Shares
agent.placeOrder(ssc); // Sell Shares
}
}
Download