设计模式(10) 门面/外观模式(简单入门 结构模式)

From Now On,Let us begin Design Patterns。

门面模式, 也称外观模式

定义

  • 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。)简言之,门面对象是外界访问子系统内部的唯一通道。 (Façade Pattern)Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.

通用类图:
这里写图片描述

角色说明:
门面角色: 客户端可以调用这个角色的方法。它知晓子系统的所有功能和责任。该角色没有实际的业务逻辑,只是一个委托类,将客户端发来的请求委派到相应的子系统去。同时只给客户端暴露客户端需要的方法,屏蔽他们用不到的方法。

子系统角色:可以同时有一个或多个子系统。每个子系统都是一个类的集合。子系统不知道门面的存在,门面相对于子系统仅是另外一个客户端而已。

适配器通用代码:
这里写图片描述

门面模式优点:

  • 减少系统的相互依赖:把外界对子系统的依赖转为对门面的依赖,与子系统无关

  • 松散耦合:门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护

  • 提高了灵活性:子系统作变化,只要不影响到门面对象即可

  • 简单易用:门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了

  • 门面模式提高了安全性:外界只能访问到门面上开通的方法,而不能触及子系统的其它业务

  • 更好的划分访问层次:通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节

门面模式的缺点:

  • 不符合开闭原则,一旦在系统投产后发现错误,就只能修改门面角色的代码,风险大需考虑,门面类很容易就成了万能类

门面模式的使用场景:

为一个复杂的模块或子系统提供一个供外界访问的接口

子系统相对立——外界对子系统的访问只要黑箱操作即可

预防低水平人员开带来的风险扩散(为其限定某一子系统,供其开发)

不想对外界暴露过多的方法,只暴露客户端用到的的方法

注意事项:

1:一个子系统可以有多个门面

门面已经在到不能忍受的程度(比如一个门面对象已经超过了200行代码),虽然都是非常简单的委托操作,也建议拆分成多个门面,否则会给以后的维护和扩展带来不必要的麻烦。如何拆分?可以按功能:如数据库操作的门面可以分为查询门面、删除门面、更新门面等。

子系统可以提供不同访问路径:以通用源码为例,ClassA、ClassB、ClassC是一个子系统中的3个对象,现在有两个不同的高层模块来访问该子系统,模块一是子系统的信任模块,可以完整的访问子系统的所有业务逻辑;而模块二属于受限访问对象,只能访问methodB方法。此时,就需要建立两个门面以供不同的高层模块来访问,即在原有代码中增加一个门面即可

public class Facade2 {
    //引用原有的门面
    private Façade façade = new Façade();
    //对外提供唯一的访问子系统的方法
    public void methodB(){
        This.facade.methodB();
    }
}

为什么要使用委托而不再编写一个委托到子系统的方法呢?那是因为在面向对象的编程中,尽量保持相同的代码只编写一遍,避免以后到处修改相似代码的悲剧。

2. 门面不参与子系统内的业务逻辑——-不参与子系统业务逻辑

以通用代码为例:如果methodC中必须先调用ClassA的doSomethingA,然后再调用 ClassC 的doSomethingC方法呢?如何改。但千万别这么做:

public void methodC( ) {
    this.a.doSomethingA();
    this.c.doSomethingC();
}

为什么不要这么做呢?因为你已经让门面对象参与了业务逻辑,而门面对象只提供了个访问子系统的一个路径而已。否则就会产生一个倒依赖的问题:子系统必须依赖门面才能被访问,门面就会跟子系统冗杂在一块了,这是设计上的一个严重错误,不仅违反了单一职责原则,同时也破坏了系统的封装性。

解决之道———继续抽象封装:建立一个封装类,封装完毕后提供给门面对象。

public class Context {
    // 委托处理
    private ClassA a = new ClassA();
    private ClassC c = new ClassC();

    // 复杂的计算
    public void complexMethod() {
        this.a.doSomethingA();
        this.c.doSomethingC();
    }
}

public class Facade {
    // 被委托的对象
    private ClassA a = new ClassA();
    private ClassB b = new ClassB();
    private Context context = new Context();

    // 提供给外部访问的方法
    public void methodA() {
        this.a.doSomethingA();
    }

    public void methodB() {
        this.b.doSomethingB();
    }

    public void methodC() {
        this.context.complexMethod();
    }
}

门面模式的例子:源自《设计模式之禅》

类图:
这里写图片描述

系统模块A B C:
这里写图片描述

门面类
这里写图片描述

这样定义一个ModuleFacade类可以有效地屏蔽内部的细节,免得客户端去调用Module类时,发现一些不需要它知道的方法。比如a2()和a3()方法就不需要让客户端知道,否则既暴露了内部的细节,又让客户端迷惑。对客户端来说,他可能还要去思考a2()、a3()方法用来干什么呢?其实a2()和a3()方法是内部模块之间交互的,原本就不是对子系统外部的,所以干脆就不要让客户端知道。

一个系统可以有几个门面类(错误的)
  在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统都有一个门面类,整个系统可以有数个门面类。

为子系统增加新行为(错误的)
  初学者往往以为通过继承一个门面类便可在子系统中加入新的行为,这是错误的。门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。比如医院中的接待员并不是医护人员,接待员并不能为病人提供医疗服务。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章