(This Post is mostly copy from: http://www.sourcetricks.com/2008/06/about-design-patterns.html)
1: Definition
Design patterns can be considered as a standardization of commonly agreed best practices to solve specific design problems.
2: Classification
Depending on the design problem they address, design patterns can be classified in different categories, of which the main categories are:
creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
3.1: Singleton
(全局唯一对象)
- Singleton is a creational design pattern.
- A design pattern to provide one and only instance of an object.
- Make the constructors of the class private.
- Store the object created privately.
- Provide access to get the instance through a public method.
- Can be extended to create a pool of objects.
C++ Code:
----------------------
Class Singleton {
private:
static Singleton* instance;
private:
Singleton()
{}
public:
Singleton* GetInstance();
};
----------------------
3.2: Factory Pattern
(中央控制,屏蔽复杂的多态类创建细节)
Factory pattern is a creational design pattern.
Idea of the factory patterns is to localize the object creation code.
This prevents disturbing the entire system for a new type introduction.
Typically when a new type is introduced in the system, change is at
one place only where the object is created to decide which constructor
to use.
Simplest of the factory is introduction of a static method in the
base class itself, which creates the required object based on the type.
Some Good Comments:
-----------
- The factory pattern is useful if you need to encapsulate create-time
polymorphism from consumers, that is, you want to provide a transparent
point from which new instances of a polymorphic type are created.
- If the type in question is not polymorphic, the factory pattern is pointless.
- If a single point of creation doesn't make sense, neither does the factory pattern.
- If it is undesirable to hide the polymorphism details away, the factory pattern is probably inappropriate, too.
- As with anything that adds complexity, you should default to not using it, but spot the point at which it is beneficial early on and apply it before it is too late.
---------
C++ Code:
----------------------
class Shape {
public:
virtual void Draw() = 0;
static Shape* Create(string type);
};
class Circle: public Shape {
public:
void Draw() {
printf("I am a circle\n");
}
friend class Shape;
};
Shape* Shape::Create(string type) {
if(type.compare("Circle")) return new Circle();
return NULL;
}
-----------------
Another version is named "Abstract Factory
", to move the functions to a separate class.
Explain:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
---------------
// Abstract Factory returning a mobile
class MobileFactory {
public:
Mobile* GetMobile(string type);
};
Mobile* MobileFactory::GetMobile(string type) {
if ( type == "Low-End" ) return new LowEndMobile();
if ( type == "High-End" ) return new HighEndMobile();
return NULL;
}
-------------------
3.3: object pool pattern
(对costly资源的包装和分配)
The
object pool pattern is a software
creational design pattern that uses a set of initialized
objects kept ready to use – a "
pool"
– rather than allocating and destroying them on demand. A client of the
pool will request an object from the pool and perform operations on the
returned object. When the client has finished, it returns the object to
the pool rather than
destroying it; this can be done manually or automatically.
Avoid expensive acquisition and release of resources by recycling
objects that are no longer in use. Can be considered a generalisation of
connection pool (database and networking) and
thread pool patterns.
Object pools employ one of three strategies to handle a request when there are no spare objects in the pool.
- Fail to provide an object (and return an error to the client).
- Allocate a new object, thus increasing the size of the pool.
- In a multithreaded environment, a pool may block the client until another thread returns an object to the pool.
3.4: Build Pattern
(对复杂Object的多态包装)
Separate the construction of a complex object from its representation,
allowing the same construction process to create various
representations.
Build Class:
Define the virtual functions to be overlapped by sub-classes.
Build Class replaces the role of Product in Director.
///////////////////////////////
4. Structural patterns
///////////////////////////////
4.1: Adapter Pattern (Wrapper Pattern)
(接口转换器)
An adapter helps two incompatible interfaces to work together. This is
the real world definition for an adapter. The adapter design pattern is
used when you want two different classes with incompatible interfaces to
work together. Interfaces may be incompatible but the inner
functionality should suit the need. The Adapter pattern allows otherwise
incompatible classes to work together by converting the interface of
one class into an interface expected by the clients.
The adapter translates calls to its interface into calls to the original interface.
The adapter is also responsible for transforming data into appropriate forms.
The
adapter is created by implementing or inheriting both the interface
that is expected and the interface that is pre-existing.
C++ Code
------------------------
Class Adapter : public PublicInterface {
private:
serviceObject* object;
public:
Adapter(serviceObject* object) {this->object = object;}
public:
void PublicInterfaceFunction(AAA){
BBB = transform(AAA);
object->SomeOtherFunction(BBB)
}
};
------------------------
4.2: Composite Pattern
(对象的树形结构)
Composite pattern is useful when you want
to treat a group of objects in the same manner as a single instance of
the object. The objects grouped, themselves could be in composite manner
in a tree fashion.
Compose objects into tree structures to represent
leaf-composite
hierarchies. Composite lets clients treat individual objects and
compositions of objects uniformly.
There are two types of components:
1) working components: Leaf
2) composite components: a group of components
4.3: Decorator Pattern
(功能增强)
Useful, when you want to add capabilities
(statically or dynamically) to an object without sub-classing the
concrete object's class as well as not affecting other objects of the
same concrete class.
The key is that the Decorate Class extends the functions of the Component but does not sub-class it. (Has-A replaces Is-A)
4.4: Facade Pattern
(统一用户接口,提供Module Level Interface)
- Facade pattern is a structural design pattern.
- Makes an existing complex software library easier to use by providing a simpler interface for common tasks.
- Allows the applications/ clients using the library de-coupled from inner workings of a complex library.
///////////////////////////////
5. Behavioral patterns
///////////////////////////////
5.1: Template Algorithm (or Method) Design Pattern
(抽象算法-伪代码)
Define the skeleton of an algorithm in an operation, deferring some
steps to subclasses. Template method lets subclasses redefine certain
steps of an algorithm without changing the algorithm's structure.
- Template pattern is a behavioral design pattern.
- This has nothing to do with C++ templates as such.
- Template patterns is a common form in object oriented programming.
Having an abstract base class (one or more pure virtual functions) is a
simple example of template design pattern.
- In the template pattern, parts of program which are well defined
like an algorithm are defined as a concrete method in the base class.
The specifics of implementation are left to the derived classes by
making these methods as abstract in the base class.
- The method which implements the algorithm is also refered as
template method and the class which implements this methods as the
template class.
C++ Example:
----------------------------
// Base class
// Template class
class Account {
public:
// Abstract Methods
virtual void Start() = 0;
virtual void Do() = 0;
virtual void End() = 0;
// Template Method
void Withdraw(int amount) {
Start();
Do
();
End
();
}
};
----------------------------
5.2: Chain of Responsibility
(火枪N联机)
Chain of Responsibility is a behavioral design pattern.
The basic idea behind this pattern is that a request or a command passes through a chain of objects until it is handled.
Objects involved in this pattern are of two types. Processing Objects and Command Objects.
Processing objects handle the commands given by command objects.
Each processing object knows what it can handle and passes the command to the next member in the chain if not handled.
A common example to understand this pattern better is the C++ exception
handling mechanism. Unhandled exceptions are passed up the call stack
until somebody acts upon it.
C++ code:
----------------------------
class ErrorHandle {
protected:
ErrorStates state;
ErrorHandle
* successor;
public:
//This handle is only responsible for an error state
ErrorHandle
(ErrorStates aState) { state = aState; }
void SetSuccessor (ErrorHandle
* handle) {
this->successor = handle;
}
//if cannot handle, then pass to the next.
//It is multiple functional calls in the stack.
virtual void ProcessError(ErrorReport& report) = 0;
};
----------------------------
5.3: Command Pattern
(用户命令的封装和历史保存)
Encapsulate a request as an object, thereby letting you parameterize
clients with different requests, queue or log requests, and support
undoable operations.
- Command pattern is a behavioral design pattern.
- Encapsulates a command/ request. The command itself is treated as an object.
- Classes participating in a command pattern include:-
- Command:- An abstract interface defining the execute method.
- Concrete Commands:- Extend the Command interface and implements the execute method. Invokes the command on the receiver object.
- Receiver:- Knows how to perform the command action.
- Invoker:- Asks the command object to carry out the request.
- Client:- Creates the commands and associates with the receiver.
- Some examples:-
- Used when history of requests is needed. (Stock orders executed for today)
- Asynchronous processing. Commands need to be executed at variant times.
- Installation wizards.
Example C++ code:
--------------
// Command interface
class Command
{
public:
virtual void execute() = 0;
};
// Receiver
class StockTrade
{
public:
StockTrade() {}
void buy() { cout << "Buy stock" << endl; }
void sell() { cout << "Sell stock" << endl; }
};
// Concrete command 1
class BuyOrder : public Command
{
StockTrade* stock;
public:
BuyOrder(StockTrade* stock)
{
this->stock = stock;
}
void execute()
{
stock->buy();
}
};
// Concrete command 2
class SellOrder : public Command
{
StockTrade* stock;
public:
SellOrder(StockTrade* stock)
{
this->stock = stock;
}
void execute()
{
stock->sell();
}
};
// Invoker
class StockAgent
{
public:
StockAgent() {}
void order( Command* command )
{
commandList.push_back(command);
command->execute();
}
private:
// Looking at this command list gives
// this order history
vector<Command*> commandList;
};
---------------------------
5.4: Visitor Pattern
(数据结构与算法分离)
the visitor
design pattern is a way of separating an
algorithm
from an object structure on which it operates. A practical result of
this separation is the ability to add new operations to existing object
structures without modifying those structures. It is one way to follow
the
open/closed principle.
- The visitor pattern is a behavioral design pattern.
- Visitor pattern allows to separate the data structures and the algorithms to be applied on the data.
- Both data structure objects and algorithm objects can evolve separately. Makes development and changes easier.
- Data structure (element) objects have an "accept" method which take in a visitor (algorithmic) object.
- Algorithmic objects have a "visit" method which take in a data structure object.
Example C++ Code
----------------------
// Forwards
class VisitorIntf;
// Abstract interface for Element objects
class ElementIntf
{
public:
virtual string name() = 0;
virtual void accept(VisitorIntf* object) = 0;
};
// Abstract interface for Visitor objects
class VisitorIntf
{
public:
virtual void visit(ElementIntf* object) = 0;
};
// Concrete element object
class ConcreteElement1 : public ElementIntf
{
public:
string name()
{
return "ConcreteElement1";
}
void accept(VisitorIntf *object)
{
object->visit(this);
}
};
// Concrete element object
class ConcreteElement2 : public ElementIntf
{
public:
string name()
{
return "ConcreteElement2";
}
void accept(VisitorIntf *object)
{
object->visit(this);
}
};
// Visitor logic 1
class ConcreteVisitor1 : public VisitorIntf
{
public:
void visit(ElementIntf *object)
{
cout << "Visited " << object->name() <<
" using ConcreteVisitor1." << endl;
}
};
// Visitor logic 2
class ConcreteVisitor2 : public VisitorIntf
{
public:
void visit(ElementIntf *object)
{
cout << "Visited " << object->name() <<
" using ConcreteVisitor2." << endl;
}
};
----------------------
5.5:
Strategy pattern
(算法切换)
In
computer programming, the
strategy pattern (also known as the
policy pattern) is a
software design pattern that enables an
algorithm's behavior to be selected at runtime. The strategy pattern
- defines a family of algorithms,
- encapsulates each algorithm, and
- makes the algorithms interchangeable within that family.
It is a straightforward design, so not detailed here.
///////////////////////////////
6. System Design Patterns
///////////////////////////////
6.0: SOLID (object-oriented design)
(设计圣经I)
It is part of an overall strategy of
agile and
adaptive programming.
1)
S: Single responsibility principle
a
class should have only a single responsibility and that responsibility should be entirely encapsulated by the class.
2)
O: Open/closed principle
software entities … should be open for extension, but closed for modification.
3)
L:
Liskov substitution principle
objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program
4)
I: Interface segregation principle
many client-specific interfaces are better than one general-purpose interface.
5)
D: Dependency inversion principle
one should “Depend upon Abstractions. Do not depend upon concretions.”
6.0: KISS principle
(设计圣经II)
Keep it simple, stupid.
6.1:Publish–subscribe Pattern
(互不关心的消息分类传递)
publish–subscribe is a
messaging pattern where senders of
messages,
called publishers, do not program the messages to be sent directly to
specific receivers, called subscribers. Instead, published messages are
characterized into classes, without knowledge of what, if any,
subscribers there may be.
Similarly, subscribers express interest in one
or more classes, and only receive messages that are of interest,
without knowledge of what, if any, publishers there are.
Benefits:
Loose coupling
Scalable
Disadvantages:
No guarantee for delivery
Possible delay
6.2: Model–view–controller
(经典的网站UI分层)
Model–view–controller (
MVC) is a software
architectural pattern for implementing
user interfaces.
It divides a given software application into three interconnected
parts, so as to separate internal representations of information from
the ways that information is presented to or accepted from the user.
[1][2] The central component, the
model, consists of application data, business rules, logic and functions. A
view
can be any output representation of information, such as a chart or a
diagram. Multiple views of the same information are possible, such as a
bar chart for management and a tabular view for accountants. The third
part, the
controller, accepts input and converts it to commands for the model or view.
In addition to dividing the application into three kinds of
components, the model–view–controller design defines the interactions
between them.
[4]
- A controller can send commands to the model to update the
model's state (e.g., editing a document). It can also send commands to
its associated view to change the view's presentation of the model
(e.g., by scrolling through a document).
- A model notifies its associated views and controllers when
there has been a change in its state. This notification allows the views
to produce updated output, and the controllers to change the available
set of commands. In some cases an MVC implementation might instead be
"passive," so that other components must poll the model for updates rather than being notified.
- A view requests information from the model that it needs for generating an output representation to the user.
6.3: Internet Layers Design Pattern
(经典的互联网分层设计)
5-Layer:
- 5: Process
& Applications
- 4: TCP/IP
- 3: Internet (MAC)
- 2: Network
- 1: Physical
7-Layer:
6.4: Event-based Design Pattern (or Event-
driven architecture
)
(事情响应机制)
There are four types of objects:
- Events
- Event List
- Event Scheduler
- Events Handler
It is widely used in Windows OS, UI, Java, etc.
6.5: Peer-to-peer Design (or Peer-to-peer architecture)
(纯分布式系统)
The most common type of structured P2P networks implement a distributed hash table (DHT),[23][24] in which a variant of consistent hashing is used to assign ownership of each file to a particular peer.[25][26] This enables peers to search for resources on the network using a hash table: that is, (key, value) pairs are stored in the DHT, and any participating node can efficiently retrieve the value associated with a given key.
Example: Chord
Chord is a protocol and
algorithm for a
peer-to-peer distributed hash table. A distributed hash table stores
key-value pairs
by assigning keys to different computers (known as "nodes"); a node
will store the values for all the keys for which it is responsible.
Chord specifies how keys are assigned to nodes, and how a node can
discover the value for a given key by first locating the node
responsible for that key.
Step 1:
IDs and keys are assigned an
-bit identifier using
consistent hashing. The
SHA-1 algorithm is the base
hashing function for consistent hashing.
Step 2:
Each node has a
successor and a
predecessor. The successor
to a node (or key) is the next node in the identifier circle in a
clockwise direction. The predecessor is counter-clockwise.
Step 3:
A logical ring with positions numbered
to
is formed among nodes. Key
k is assigned to node successor(
k), which is the node whose identifier is equal to or follows the identifier of
k. If there are
N nodes and
K keys, then each node is responsible for roughly
keys.
Step 4:
With high probability, Chord contacts
nodes to find a successor in an
-node network.
Example: Chord Design