Design Pattern Quick Overview
心得:
1. 确实存在的dependency只能被转移,不能被去除。 通过“转移”来降低coupling(耦合度)。
2. Inversion of control和dependency injection概念接近(don't call me, I will call you)。dependency inversion则非常不同:instead of letting high-level component directly depends on low-level component, now the structure goes to be:
high-level component plus adapter Interface (it defines what are required from low-level needed for high-level)
low-level component plus adaptee class : adapter interface
In this way, it looks like that low-level now dpends on high-level so it seems to happen "dependency reversion". The key to make it work is: various patterns such as Plugin, Service Locator, or Dependency Injection are then employed to facilitate the run-time provisioning of the chosen low-level component implementation to the high-level component.
3. Dependencies can be registered, bound, located, externally injected, etc., by many different means. Hence, moving dependency management from one module to another can be accomplished in a plenty of ways. However, there should exist a definite reason for moving a dependency away from the object that needs it because doing so can complicate the code hierarchy to such an extent that its usage appears to be "magical".
关于design pattern的资料实在是太多太多了。这里只列出两个我用到的:
1) 一句话总结各个pattern,coolshell上有一篇写得挺好。
一个个来
1. Strategy(policy)
Take validate incoming data for example. there are many data types which require different validation methods. Here, if using strategy pattern, we will have a context object which can accept IStrategy (as interface or first-class function) and it's the master to actually execute the validation; and there are several strategy implementation classes (or functions). By passing different validation strategy into context class, different validation process can be executed.
2. Facade
Well, it's far easier than expected. By organizing client frequent logic into a facade class which is the bridge between client and the subsystem, client only depends on the facade class to make use of subsystem functionality. Below diagram tells so clearly:
3. Template method
Parent class controls the workflow of doing something, while each step can be overridden by sub classes in executing the workflow in parent class.
4. Adapter(wrapper)
Its intent is: without modifying plenty of places (modules) using a legacy class and still make the class work well in a new situation with more graceful interface, in the new situation (calling place), use its adapter class (which treats the legacy class as its adaptee) instead of directly using the legacy class, while internally the adapter class just does some wrap work with the legacy class to provide a better interface for adapting to the new situation. For the new client, it uses the adapter class of course.
wiki's "(LegacyLine + LegacyRectangle) ++ (Line + Rectangle)" JAVA example illustrates the reality clearly.
5. Command (action, transaction)
个人觉得,这个模式的意义在于提出“command”的概念从而使得很多问题都可以通过加上这么非常通用的一层对象来解耦。command模式,理解下来,主要是两点:1)解耦invoker和receiver,使得他们可以完全被隔离。比如switch和light/engine,比如menuitem和document/application;2)支持undo。
两个认识(在理解command模式的过程中):1)两个模块之间有依赖关系(只要不是冗余的、可以去掉的), 这层关系是不能被去除的。只是看实现方法上是把两个模块紧耦合还是松散耦合在一起。design pattern做的事情就是:根据其适用的场合,通过对象间结构或行为的改变来转移“依赖”,使得耦合度大大降低。2)对于command模式而言,以switch为例。switch on/off和必须要引发的结果之间是必然的因果关系。最紧耦合的写法是在switch对象的on/off函数里直接写比如light on/off, 或者engine on/off;再进一步的话,用个command类包装出那部分逻辑,从而使得switch只依赖于command,从而把对具体操作对象的“依赖”转移到了command类里;再进一步,在command类的实现里,不要直接操作具体对象来on/off,而是通过fire event的方式在switch发送on/off event的时候触发command的on/off来调用事先注册好的各具体操作对象的delegate。这样,最终,实现了一个非常松散耦合的结构。A benefit of this particular implementation of the command pattern is that by making the switch not aware of the lamp/light, the switch can be used with any device, not just a light.
6. State
从结构上,类似strategy模式:一个是通过切换状态来改变对象行为,另一个是通过切换算法来改变对象行为。 这个模式的几个关键点:
1) 首先,要明白participants包括:context, state, concrete states以及client。client来调用context,并且state应该对他是透明的,状态的切换带来的context对象行为的改变看上去是magic;context持有current state,并使用它来issue用户的request,他一般不负责state的切换;各个具体的state对象,干实事的,GOF建议的是每一个state应该知道它的next state从而把state switch的工作分摊到每一个state里。
2)state的适用场合:一个是对象的行为依赖于其当前状态,并且要求要在运行时动态的改变其行为;if-else深层次嵌套的场合。
7. Observer
C#的delegate/event处理机制和MFC的文档和view的一致性处理机制(UpdateAllViews)都是observer的典型应用。参与者是两类:subject和observer;关系是:observer们对subject的改变感兴趣,希望在subject做出改变的时候后被notify。做法非常直接:把observer本身注册到subject里 (以IObserver或者delegate、函数指针的callback形式)。It is mainly used to implement distributed event handling systems.
8. Iterator
参与者有三:client,aggregate以及iterator;关系是:client想要遍历aggregate的内容(其内部可能以各种数据结构存储数据:hashtable,linkedlist,array,etc),又不想暴露aggregate的遍历细节(因为依赖于存储数据的数据结构或复杂性),就用iterator对象来封装了对aggregate遍历的细节,只暴露友好的、抽象的first(),next()遍历接口给client。
iterators are used to access the elements of an aggregate object sequentially without exposing its underlying implementation. An Iterator object encapsulates the internal structure of how the iteration occurs.
9. Vistor
我对它总体上的理解:element.accept(visitor)和visitor.visit(concreteElement)是这个模式工作的关键。有些时候,直接对element继承体系进行修改(加virtual function)来实现对各个element的新的使用是不被允许的。这时候,创建visitor类+对element体系加入简单的accept virtual function就可以把新的访问element的需求转移到visitor类中,并可以简单的加入新的visitor类提供新的visit方法。
From wiki:
the visitor design pattern is a way of separating an algorithm from an object structure it operates on. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.
In essence, the visitor allows one to add new virtual functions to a family of classes without modifying the classes themselves; instead, one creates a visitor class that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.
参与者有:element和visitor;
一般和composite pattern一起用;
double dispatch:实际调用visitor.visit的时候,这个function可以使用的是两个具体对象:concreteVisitor和concreteElement。
(note about below diagram: Instead of creating "print" methods for each subclass (Wheel, Engine, Body, and Car), a single class (CarElementPrintVisitor) performs the required printing action.)
http://en.wikipedia.org/wiki/Visitor_pattern
http://www.javaworld.com/javaworld/javatips/jw-javatip98.html
10. Bridge
两个关键点先:
1. It is meant to "decouple an abstraction from its implementation so that the two can vary independently".
2. the bridge pattern is useful when both the class as well as what it does vary often. The class itself can be thought of as the implementation and what the class can do as the abstraction. The bridge pattern can also be thought of as two layers of abstraction.
Not well understand. Just in one case it would be applied widely: for a frequent referred + unstable class especially living on framework level of the app, we always use pImp / bridge pattern to avoid as many detail as possible exposed into header file (c++ only) to let this class's clients won't be bothered by frequent changes in it.
11. Chain of Responsibility
Participants: to-be-processed objects (data), processing objects, client. The behavior is easy to understand and organize: firstly, client builds up the chain of processing objects by calling ProcessingObject::SetSuccessor(); secondly, wrap the to-be-processed object; pass it to the first processing object in the chain to kick off its processing. In normal design, all these tree roles are needed BUT intead of building up the chain to process, we always create a new function OR class to do below work:
void ProcessObject(Request request)
{
int flag = request.evalFlag();
if(flag < 100$)
{// go for processing objectA}
else if(flag < 1000$)
{// go for processing objectB}
else if(flag < 10000$)
{// go for processing objectC}
}
12. Composite
The key point for this pattern is: client intends to operate a collection of objects just the same as a single object. The solution is an interface that allows treating complex and primitive objects uniformly. The composite pattern describes that a group of objects are to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
Participants: component, leaf : component, composite : component, client.
13. Flyweight
Participants: FlyweightFactory, Flyweight, client. This pattern behavior is: whenever client wants a flyweight object, he will make use of singleton flyweightFactory to get: flyweightFactory::get(), inside which: it will query its hashMap serving as a flyweight pool firstly; if found, just return the already created object; otherwise, create a new one and add it into the hashmap pool.
14. Mediator
Participants: Concretemediator : Mediator, colleague, client. The pattern behavior is: client creates both mediator and colleague, and set mediator into colleague,and then colleague will do its works while inside it directly call mMediator.XX(). Some key points:
1. Facade模式是解耦系统外到系统内(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。
15. Memento
a class's state can be stored into another object whose ONLY responsibility is to store specific class's state.
Participants: Originator, Memento, client (careTaker). The pattern behavior is: client will new and use Originator to do his work; during the whole process, of course originator state will be changed; if client wants to save some states at any point, he can call originator.SaveToMemento() in which the state data will be stored into a new created memento object and returned. At last, when he wants a former state back, just call originator.RestoreFromMemento().
Key points:
1) Memento should be the container to cache orginator's state which can be either easy or complex data / info.
2) Originator should implement SaveToMemento() and RestoreFromMemento().
16. Proxy
一句话:为其他对象提供一种代理以控制对这个对象的访问。参与者:proxyObject,realObject,client.
举例两个具体情况:
(1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很 迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.
(2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.
总之原则是,对于开销很大的对象,只有在使用它时才创建。
17. Decorator
The decorator pattern can be used to make it possible to extend (decorate) the functionality of a certain object at runtime, especially like below way to decorate a object:
有图有真相:
18. Prototype
个人理解的几点:
0. 何谓prototype?这里指的是一个需要被相当数量的copy的对象,那么这个对象一定程度上就变成了一个model,一个原型对象。提供一种方法可以方便高效的获取它的一个copy是它的出现动因。
1. 它的本质在于解决某些场景下确实需要一个对象的true copy的问题。这种情况下,singleton不能满足需求(因为我们要对对象的一份copy操作而不是这个对象本身),“new”来全新创建一个对象不方便。
2. 它的实现其实非常直观:只需要被copy的对象实现clone()方法。对于c#来说,已经有protected memberwiseclone方法 + Icloneable接口来从语言本身支持了prototype模式。
3. When creating an object is time consuming and a costly affair and you already have a most similar object instance in hand, then you go for prototype pattern.
19. Builder
- Builder:Abstract interface for creating objects (product).
- Concrete Builder: Provides implementation for Builder. It is an object able to construct other objects. Constructs and assembles parts to build the objects.
- Director: The Director class is responsible for managing the correct sequence of object creation. It receives a Concrete Builder as a parameter and executes the necessary operations on it.
- Product:The final object that will be created by the Director using Builder.
20-22. Factory, Abstract Factory, Singleton
先到这里,告一段落。