设计模式之结构型模式

  第一篇:设计模式之创建型模式

  

  在这部分里,我们关注GoF里面的结构型模式,它主要是用于描述如何将类组合在一起去构成更大的结构。结构型模式包括适配器(Adapter)、装饰(Decorator)、桥接器(Bridge)、享元(FlyWeight)、门面(Facade)、合成(Composite)以及代理(Proxy)模式。

  下面我们对上面提到的模式分别进行描述。

  1)适配器(Adapter)。当我们已经开发出一个模块,有一套清晰的接口,并且模块正在被某个功能使用(意味着模块接口改变的可能性不高),这是如果有另外一个功能也需要使用这个模块的功能,但是对应的是一套完全不同的接口,这时适配器就可以发挥作用了。

  适配器模式分为两种,一种是对象适配器,一种是类适配器,对象适配器的UML图如下:

  

  这里Adaptee1和Adaptee2指两套不同的子系统,它们作为Adapter的属性存在,可以使用IoC的方式指定。

  类适配器的UML图如下:
  

  同样是两个不同的子系统,但是这里我们创建了2个Adapter类来分别指向两个子系统。在这里我们可以在Client和ITarget之间,设置一个Adapter工厂,来根据业务需求创建不同的Adpater实例。
  2)装饰(Decorator),假如我们已经开发了一套功能,然后根据需求,需要增加一些子功能,而且这些子功能是比较分散比较时可以增删的,这时如果直接修改接口,那么会造成接口功能复杂并且不稳定,针对这种情况,我们可以使用装饰模式。对应的UML图如下:

  

  上图中,ConcreteComponent已经实现了Component的基本功能,对于一些附加的功能,如果放在ConcreteComponent中不合适的话,我们可以像ConcreteDecoratorA一样,创建一个基于Decorator的类,通过SetComponent方法将核心功能和辅助功能串在一起。

  有时,为了简单,我们也可以把ConcreteDecorator直接挂在Concretecomponent下面。

  3)桥接器(Bridge),面向对象提倡的几个最佳实践包括:1)封装变化;2)面向接口编程;3)组合优于继承;4)类的职责尽量单一。桥接器完美的体现了这些,通过创建型模式,我们可以很好地达到面向接口编程的目标,也就是说我们在程序中各变量的声明类型是接口类型或者抽象类,而具体的实现类型则由不同的设计模式使用不同方式指定。这在接口或者抽象类基本稳定的情况下,是很好地,但当接口需要发生变化时,我们如何去处理?可以看看桥接器的UML图:

  

  通过这个图,我们可以看出,Implementor接口的变化,对于Client来说,基本是没有影响的。Abstraction会持有Implementor的一个实例。

  4)享元(FlyWeight),当我们系统中需要使用大量的小对象,但我们又不希望将所有的小对象都创建出来时,可以考虑使用享元模式,它会抽取小对象中的公共部分,将其封装为基类,然后针对不同条件创建小对象,同时在对象池中维护这些小对象,客户在需要使用小对象时,首先在对象池中查找,如果存在,直接返回。对于小对象中“个性”的部分,由调用小对象的客户端进行维护。对应的UML图如下:
  

  除了上述的简单享元,还存在一种复合享元,对应的UML图如下:

  

  图中,CompositeConcreteComponent是不共享的,但是它里面包含很多简单的享元,这些享元是共享的,我们可以把它想象成一个特殊的“享元工厂”。

  通常提到享元,最常见的例子就是文本编辑器中的26个字母,在.NET中,字符串常量也使用了享元模式。

  在享元模式中,我们通常会将FlyWeightFactory设计为单例模式,否则享元就没有意义了。

  5)门面(Facade),如果我们的程序需要深入调用某个模块的内部,但我们又不想和模块过紧耦合,这时可以考虑使用门面模式,来对外部封装内部子系统的实现。简单的门面可能和代理在某种程度上很相似。

  门面模式没有固定的UML图,它是根据客户端的实际需求以及子系统内部的接口来确定的。

  6)合成(Composite),当我们的对象结构中存在“父子”关系时,可以考虑使用合成模式。它分为两种,一种是安全型的合成模式,UML图如下:

  

  这种类型的合成模式,对于Component的增、删、改,都在Composite中维护,Leaf根本不知道这些操作。另一种是透明型的合成模式,UML图如下:

  

  这种类型的合成模式,自上而下所有的Component都会有增、删、改的操作,只不过对于Leaf来说,这些操作时没有意义的。

  7)代理(Proxy),在编写程序时,有时我们希望使用某个对象或者模块的功能,但是因为种种原因,我们不能直接访问,这时就可以考虑使用代理,对应的UML图如下:

  

  需要注意的是,在这里RealSubject只有一个,如果有多个,那么就是Adapter了。另外,代理也可以加入自己的一些逻辑处理,例如PreExecute和PostExecute。如果这里有多个Proxy,那么就是Decorator了。

  

  上面就是对结构型设计模式的快速浏览,其中有很多UML图看上去很相似,但深入去思考,每个模式的出发点、所要解决的问题是不一样的。

posted @ 2013-04-27 13:38  李潘  阅读(4013)  评论(0编辑  收藏  举报