文章目录
第四章和第五章陆续学习了一些设计模式,其中Adapter,Decorator,Facade和Proxy模式具有相似的结构。这篇博客将对这四个设计模式的共性与区别进行分析,为开发过程中设计模式的选择提供启发。
共性
使用组合和委派
Adapter、Decorator、Facade与Proxy设计模式之间的主要相似之处在于其结构。它们都使用了组合和委派。即它们都包装了一个对象,并将请求处理或方法的调用委托给它们:
-
Adapter模式通过增加一个接口,将将已存在的子类封装起来。client面向接口编程,适配器将adapter接口的功能委托给被封装的adaptee子类。
-
Decorator模式包装一个被装饰的对象并在其之上实现行为,将基础的功能委托给被装饰的对象实现,增加的装饰功能在包装类中实现。
-
Facade模式提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统做了一个封装,简化客户端使用。当调用外观接口时,将相应的功能委托给小接口中的方法实现。
-
Proxy模式包装一个实体,并将代理对象中的方法委托给实体实现。
以上四种设计模式在结构上相似,即它们都使用了组合和委派。这种结构使得它们都给另一对象提供了一定程度上的间接性,因而有利于系统的灵活性。
结构型模式
在Design Patterns: Elements of Reusable Object-Oriented Software 一书中,上述设计模式都被定义为结构型模式,即组合类和对象以获得更大的结构的设计模式。结构型对象模式描述了如何对一些对象进行组合,从而实现新功能的一些方法。
-
Adapter模式使得一个接口(adaptee的接口)与其他接口兼容,从而给出了多个不同接口的统一抽象。为此,类适配器对一个adaptee类进行私有继承。这样,适配器就可以用adaptee的接口表示它的接口。
-
Decorator(4.4)模式描述了如何动态地为对象添加职责。Decorator模式是一种结构型模式。这一模式采用递归方式组合对象,从而允许你添加任意多的对象职责。例如,一个包含用户界面组件的Decorator对象可以将边框或阴影这样的装饰添加到该组件中,或者它可以将窗口滚动和缩放这样的功能添加的组件中。我们可以将一个Decorator对象嵌套在另外一个对象中就可以很简单地增加两个装饰,添加其他的装饰也是如此。因此,每个Decorator对象必须与其组件的接口兼容并且保证将消息传递给它。Decorator模式在转发一条信息之前或之后都可以完成它的工作(比如绘制组件的边框)。
-
Facade模式则描述了如何用单个对象表示整个子系统。模式中的facade用来表示一组对象,facade的职责是将消息转发给它所表示的对象。
-
在Proxy模式中,proxy对象作为其他对象的一个方便的替代或占位符。它的使用可以有多种形式。例如它可以在局部空间中代表一个远程地址空间中的对象,也可以表示一个要求被加载的较大的对象,还可以用来保护对敏感对象的访问。Proxy模式还提供了对对象的一些特有性质的一定程度上的间接访问,从而它可以限制、增强或修改这些性质。
局部的共性
Decorator模式和Proxy模式的共性
- 都实现了被包装类的全部接口
- 都可以很容易地在被包装类的方法之外加上自定义的方法
- 都描述了怎样为对象提供一定程度上的间接引用,实现部分都保留了指向另一个对象的指针,它们向这个对象发送请求
Adapter和Facade模式的共性
- 都具有与它们所包装的类不同的接口
- 都使两个不同的类/对象相互兼容,以进行通信。
区别
用途的不同
这些模式的不同之处主要在于用途。
- Adapter模式主要是为了将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- Decorator模式主要是为了在运行时动态地给一个对象添加一些额外的职责,使得一个对象被创建之后也可以继续丰富其功能。
- Facade模式既不转换接口也不添加新功能,而是为子系统中的一组接口提供一个一致的接口。它定义了一个高层接口,这个接口使得这些子系统更加容易使用。客户不是直接访问系统的各个组件,而是通过高层接口调用各个组件,从而用更简单的接口和更少的工作量与一个复杂的系统进行交互。
- Proxy模式的结构也非常类似于Adapter 和 Decorator,但其目的是为其他对象提供一种代理以控制对这个对象的访问。代理阻止客户端直接访问实体,而是提供替代行为或将请求转发给实体对象。Proxy模式是上述设计模式中最通用的模式,它可以以不同的方式使用,例如用于与远程对象通信的远程代理,用于控制昂贵对象访问的虚代理,用于提供对对象访问保护的保护代理,高速缓存代理,可以返回缓存的对象等。
是否转换接口
- Adapter模式和Facade模式转换接口,使两个截然不同的类/对象相互兼容,以进行通信。
- Decorator模式和Proxy模式不转换接口,它们仅实现原始对象的接口,因此它们可以将其传递给接受原始对象的方法,并在此基础上在被包装类的方法之外加上自定义的方法。
能否动态分离性质
像Decorator模式一样,Proxy模式构成一个对象并为用户提供一致的接口。但与Decorator模式不同的是,Proxy模式不能动态地添加或分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者。
被包装类的责任
- 在Adapter模式中,被包装类提供的功能与适配器接口提供的功能完全相同,转换接口后就可以通过适配器被客户端间接调用。
- 在Decorator模式中,组件仅提供了部分功能,而一个或多个Decorator负责完成其他功能。
- 在Facade模式中,被包装的各个接口的功能与封装后统一的接口提供的功能完全相同,通过间接调用各个被封装接口的方法可以完成全部的实际操作,封装接口不提供新功能。
- 在Proxy模式中,实体类定义了关键功能,而Proxy提供(或拒绝)对它的访问。
包装类的责任
- 在Adapter和Facade模式中,包装类将外部的调用委托给被被包装类实现,不提供新的功能。
- 在Decorator模式中,包装类将对基础功能的调用委托给被包装类实现,同时在包装类中提供新的功能。
- 在Proxy模式中,实体类定义了关键功能,而包装实体类的代理提供(或拒绝)对它的访问。
包装类的构造方法
- Proxy模式中,代理类的构造方法不包装现有对象,构造方法不需要传入现有对象。
- Decorator和Adapter包装已经存在的对象,并且通常在构造方法中提供此类对象。
- Facade构造方法采用整个对象图的根元素,否则它看起来将与Adapter相同。
链接关系
- Decorator模式允许添加多个功能,并可以通过链接多个装饰器以有序的方式进行操作。
- Proxy模式不建议进行对象代理的链接。
写在最后
上述四个设计模式的结构具有一定的共性。虽说在本篇博文的第二部分中写了很多区别,但它们根本的区别在于用途不同。了解途的不同将为我们选择合适的设计模式提供启发。
简而言之,如果需要转换接口以使本由于接口不兼容而不能一起工作的那些类可以一起工作,则应使用Adapter设计模式;如果要为子系统中的一组接口提供一个一致的接口,用更简单的接口和更少的工作量与一个复杂的系统进行交互,则应使用Facade模式;使用Decorator模式可以在运行时在现有对象上添加新行为,它提供了混合行为的灵活性,并可以根据客户端要求以不同顺序应用它们;如果由于各种原因(例如安全性,性能,网络等)需要隐藏真实对象,则应使用Proxy模式。