设计模式6:“接口隔离”模式——Façade,Proxy,Adapter,Mediator

接口隔离”模式

  在组建构建过程中,某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接接口(稳定的),来隔离本来相互紧密关联的接口是一种常见的解决方案。

典型模式: Façade,Proxy,Adapter,Mediator


Façade

系统间耦合的复杂度

动机(Motivation)

  • 上述方案A的问题在于组件的客户和组件中的各种复杂的子系统有了过多的耦合,随着外部客户程序和子系统的演化,这种过多的耦合面临很多变化的挑战。
  • 如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦。

模式定义 

为子系统中的一组接口提供一个一致(稳定)的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用(复用)。 

                                                                                                                                                     --《设计模式》Gof

要点总结: (将子系统的变化限制在一个范围内)

  • 从客户程序的角度来看, Façade 模式简化了整个组件系统的接口。对于内部和外部客户程序来说, Façade 模式实现了一种解耦的效果——即内部的子系统的任何变化不会影响到 Façade 接口的变化。
  • Façade 设计模式更注重从架构的层次去看待整个系统,而不是单个类的层次。Façade 很多时候更是一种架构设计模式。
  • Façade 设计模式并非是一个集装箱,可以任意的放入任何多个对象。Facade模式中组件的内部应该是“相互耦合关系比较大的一系列组件”,而不是简单的功能集合,以便能够实现松耦合,高内聚的特性。

代理模式(Proxy)

 动机(Motivation)

  • 在面向对象系统中,有些对象由于某种原因(比如对象创建开销很大或者某些操作需要安全控制或者需要进程外访问等),直接访问会给使用者、或者系统结构带来很多的麻烦。
  • 如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方法。 
模式定义 
为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问。 

                                                                                                         --《设计模式》Gof 

  在软件的开发过程中,基于某种原因,直接访问有些对象会给使用者或者系统带来很多麻烦(比如创建对象的开销、相关的安全机制、进程之外的访问等等)。这样的情况下,如果我们希望按照原有的方式(透明操作)去访问这个对象,但又希望回避这些“麻烦”,我们就倾向于建立一个 Proxy 来作为原有对象和访问者的中间层。通过这个中间层,我们可以对相关的额外内容进行处理(比如添加一些安全性的验证等等),从而实现了在不失去对对象的透明操作的情况下,同时管理和控制这些对象特有的复杂性。 

 

具体代码如下: 
 1 class ISubject{
 2 public:
 3     virtual void process();
 4 };
 5 
 6 
 7 class RealSubject: public ISubject{
 8 public:
 9     virtual void process(){
10         //....
11     }
12 };
13 
14 class ClientApp{
15 
16     ISubject* subject;
17 
18 public:
19 
20     ClientApp(){
21         subject=new RealSubject();
22     }
23 
24     void DoTask(){
25         //...
26         subject->process();
27 
28         //....
29     }
30 };
31 
32 
33 class ISubject{
34 public:
35     virtual void process();
36 };
37 
38 
39 //Proxy的设计
40 class SubjectProxy: public ISubject{
41 
42 
43 public:
44     virtual void process(){
45         //对RealSubject的一种间接访问
46         //....
47     }
48 };
49 
50 class ClientApp{
51 
52     ISubject* subject;
53 
54 public:
55 
56     ClientApp(){
57         subject=new SubjectProxy();
58     }
59 
60     void DoTask(){
61         //...
62         subject->process();
63 
64         //....
65     }
66 };
View Code

要点总结:

  • “增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方案。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的Proxy对象便是解决这一问题的常用手段。
  • 具体Proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组建模块提供抽象代理层,在架构层次对对象做proxy
  • proxy并不一定要求保持接口完整的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。 

适配器(Adapter)

动机(Motivation)

  • 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
  • 如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?
模式定义 
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
 --《设计模式》Gof
结构
 
 
  生活中如电源适配器,vga转换线等,适配器模式可以通过将旧的类接口转化为新接口,从而解决新环境要求的接口问题。
具体代码如下:
 1 //目标接口(新接口)
 2 class ITarget{
 3 public:
 4     virtual void process()=0;
 5 };
 6 
 7 //遗留接口(老接口)
 8 class IAdaptee{
 9 public:
10     virtual void foo(int data)=0;
11     virtual int bar()=0;
12 };
13 
14 //遗留类型
15 class OldClass: public IAdaptee{
16     //....
17 };
18 
19 //对象适配器
20 class Adapter: public ITarget{ //继承
21 protected:
22     IAdaptee* pAdaptee;//组合
23     
24 public:
25     
26     Adapter(IAdaptee* pAdaptee){
27         this->pAdaptee=pAdaptee;
28     }
29     
30     virtual void process(){
31         int data=pAdaptee->bar();
32         pAdaptee->foo(data);        
33     }       
34 };
35 
36 
37 //类适配器
38 class Adapter: public ITarget,
39                protected OldClass{ //多继承               
40                
41 }
42 
43 int main(){
44     IAdaptee* pAdaptee=new OldClass();    
45     
46     ITarget* pTarget=new Adapter(pAdaptee);
47     pTarget->process();   
48     
49 }
50 
51 
52 class stack{
53     deqeue container;
54     
55 };
56 
57 class queue{
58     deqeue container;    
59 };
adpter

要点总结:

  • Adapter模式主要应用于“希望复用一些现存的类,但是接口又与服用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
  • GoF23定义了两种Adapter模式的结构实现:对象适配器好类适配器。但类适配器采用“多继承”的实现方式,一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合的精神。
  • Adapter模式可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。 

中介者(Mediator)

动机(Motivation)

  • 在软件构建的过程中,劲吹出现多个多项互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到了一些需求的更改,这种直接的饮用关系将面临不断的变化。在这种情况下,我们可以使用“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。
  • 在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间额紧耦合引用关系,从而更好地抵御变化。

模式定义 

  用一个中介对象来封装(封装变化)一系列的对象交互。中介者使各个对象不需要显示的相互引用(编译时依赖 -> 运行时依赖),从而使其耦合松散(管理变化),而且可以独立的改变他们之间的交互。                                                                                                                     

  --《设计模式》Gof                                       

结构

 

要点总结

  • 将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变“多个对象互相关联”为“多个对象和一个中介者关联”,简化了系统的维护,地狱了可能的变化。
  • 随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。
  • Facade模式是解耦系统间(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。

 

本文内容源自 :C++设计模式 Design Patterns 李建忠  课程

posted on 2017-12-09 13:16  flysong  阅读(232)  评论(0编辑  收藏  举报

导航