设计模式之门面模式

一 概念

列表内容

外部与一个子系统的通信必须通过一个统一的门面对象进行,这就是门面模式。

二实现

还是先举个例子,大家都写过信吧,现在让我们来模拟一下写信的整个过程:
先写信的内容–>然后写信封–>然后把信放到信封中封好–>投递到信箱中.
设计模式之门面模式_第1张图片
普通方法实现
整个过程还过程还算是简单的,看一下代码如何实现:
public interface ILetterProcess {
//首先要写信的内容
public void writeContext(String context);
//其次写信封
public void fillEnvelope(String address);
//把信放到信封里
public void letterInotoEnvelope();
//然后邮递
public void sendLetter();
}
写信过程的实现
public class LetterProcessImpl implements ILetterProcess {//写信
public void writeContext(String context) {
System.out.println(“填写信的内容…” + context);
}
//在信封上填写必要的信息
public void fillEnvelope(String address) {
System.out.println(“填写收件人地址及姓名…” + address);
}
//把信放到信封中,并封好
public void letterInotoEnvelope() {
System.out.println(“把信放到信封中…”);
}
//塞到邮箱中,邮递
public void sendLetter() {
System.out.println(“邮递信件…”);
}
}
在这种环境下,最累的是写信人,为了发送一封信要有4个步骤,而且这4个步骤还不能颠倒,我们先看看这个过程如何通过程序表现出来,有人开始用这个过程写信了:
public class Client {
public static void main(String[] args) {
//创建一个处理信件的过程
ILetterProcess letterProcess = new LetterProcessImpl();
//开始写信
letterProcess.writeContext(“Hello,It’s me,do you know who I am? I’m
//开始写信封
letterProcess.fillEnvelope(“Happy Road No. 666,God Province,Heaven”
//把信放到信封里,并封装好
letterProcess.letterInotoEnvelope();
//跑到邮局把信塞到邮箱,投递
letterProcess.sendLetter();
}
}
运行结果如下所示:
填写信的内容…Hello,It’s me,do you know who I am? I’m your old lover. I’d like to…
把信放到信封中…
邮递信件…

我们回过头来看看这个过程,它与高内聚的要求相差甚远,你想想,你要知道这4个步骤,而且还要知道它们的顺序,一旦出错,信就不 可能邮寄出去,这在面向对象的编程中是极度地不适合,它根本就没有完成一个类所具有的 单一职责。
门面模式实现
投递1封信好说,但是如果10封信呢,100封信呢,更多的信呢?这不得累死!有好消息了!现在呢,邮局开放了一个新的业务:邮局找了一个代理人,也就是说,以后你告诉代理人信的内容和地址,后面的过程你就不用管了,代理人全就给你办了,这样是不是简单多了。那好,让我们看一下,这个过程用代码如何实现!
设计模式之门面模式_第2张图片
类图中增加了一个ModenPostOffice类,负责对一个比较复杂的信件处 理过程的封装,然后高层模块只要和它有交互就成了
public class ModenPostOffice {
private ILetterProcess letterProcess = new LetterProcessImpl();
//写信,封装,投递,一体化
public void sendLetter(String context,String address){
//帮你写信
letterProcess.writeContext(context);
//写好信封
letterProcess.fillEnvelope(address);
//把信放到信封中
letterProcess.letterInotoEnvelope();
//邮递信件
letterProcess.sendLetter();
}
}

客户只要把信的内容以及收信地址给邮局代理人,他们就会把信写好,封好,并发送出去。这种服务推出后大受欢迎,这多简单,客户减少了很多工作,谁不乐意呀。那我们看看客户是怎么调用的:
public class Client {
public static void main(String[] args) {
//现代化的邮局,有这项服务,邮局名称叫Hell Road
ModenPostOffice hellRoadPostOffice = new ModenPostOffice();
//你只要把信的内容和收信人地址给他,他会帮你完成一系列的工作
//定义一个地址
String address = “Happy Road No. 666,God Province,Heaven”;
//信的内容
String context = “Hello,It’s me,do you know who I am? I’m your old
//你给我发送吧
hellRoadPostOffice.sendLetter(context, address);
}
}
运行结果是相同的。我们看看场景类是不是简化了很多,只要与ModenPostOffice交互就成了,其他的什么都不用管,写信封啦、写地址啦……都不用关心,只要把需要的信息提交 过去就成了,邮局保证会按照我们指定的地址把指定的内容发送出去,这种方式不仅简单, 而且扩展性还非常好,如果现在要增加一个功能,寄往God Province(上帝省)的邮件都必须进行安全检查,那我们就很好处理了。
增加了一个Police类,负责对信件进行检查,如代码清单23-6所示。 代码清单23-6 信件检查类

public class Police {
//检查信件,检查完毕后警察在信封上盖个戳:此信无病毒
public void checkLetter(ILetterProcess letterProcess){ System.out.println(letterProcess+" 信件已经检查过了...");
}
}

我们再来看一下封装类ModenPostOffice的变更,它封装了这部分的变化:
public class ModenPostOffice {
private ILetterProcess letterProcess = new LetterProcessImpl();
private Police letterPolice = new Police();
//写信,封装,投递,一体化了
public void sendLetter(String context,String address){
//帮你写信

    letterProcess.writeContext(context);
    //写好信封
    letterProcess.fillEnvelope(address);
    //警察要检查信件了
    letterPolice.checkLetter(letterProcess);
    //把信放到信封中
    letterProcess.letterInotoEnvelope();
    //邮递信件
    letterProcess.sendLetter();
    }
}

只是增加了一个letterPolice变量的声明以及一个方法的调用,那这个写信的过程就变成这样:先写信、写信封,然后警察开始检查,之后才把信放到信封,最后发送出去,那这个 变更对客户来说是透明的,他根本就看不到有人在检查他的邮件,他也不用了解,反正现代 化的邮件系统都帮他做了,这也是他乐意的地方。

高层模块没有任何改动,但是信件却已经被检查过了。这正是我们设计所需要的模式, 不改变子系统对外暴露的接口、方法,只改变内部的处理逻辑,其他兄弟模块的调用产生了 不同的结果,确实是一个非常棒的设计。这就是门面模式。

再来看看门面模式的概念:要求一个子系统的外部与其内部的通信必须通 过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

三门面模式的应用

门面模式有如下优点:
● 减少系统的相互依赖 想想看,如果我们不使用门面模式,外界访问直接深入到子系统内部,相互之间是一种强耦合关系,你死我就死,你活我才能活,这样的强依赖是系统设计所不能接受的,门面模 式的出现就很好地解决了该问题,所有的依赖都是对门面对象的依赖,与子系统无关。
● 提高了灵活性 依赖减少了,灵活性自然提高了。不管子系统内部如何变化,只要不影响到门面对象,任你自由活动。
● 提高安全性 想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到。
门面模式的缺点
门面模式最大的缺点就是不符合开闭原则,对修改关闭,对扩展开放,看看我们那个门 面对象吧,它可是重中之重,一旦在系统投产后发现有一个小错误,你怎么解决?完全遵从 开闭原则,根本没办法解决。继承?覆写?都顶不上用,唯一能做的一件事就是修改门面角 色的代码,这个风险相当大,这就需要大家在设计的时候慎之又慎,多思考几遍才会有好收获。
门面模式的使用场景
● 为一个复杂的模块或子系统提供一个供外界访问的接口
● 子系统相对独立——外界对子系统的访问只要黑箱操作即可 比如利息的计算问题,没有深厚的业务知识和扎实的技术水平是不可能开发出该子系统的,但是对于使用该系统的开发人员来说,他需要做的就是输入金额以及存期,其他的都不 用关心,返回的结果就是利息,这时候,门面模式是非使用不可了。
● 预防低水平人员带来的风险扩散 比如一个低水平的技术人员参与项目开发,为降低个人代码质量对整体项目的影响风险,一般的做法是“画地为牢“,只能在指定的子系统中开发,然后再提供门面接口进行访问 操作。
门面不参与子系统内的业务逻辑
我们这节的标题是什么意思呢?我们举一个例子来说明,我们把门面上的methodC上的逻辑修改一下,它必须先调用ClassA的doSomethingA方法,然后再 调用ClassC的doSomethingC方法,如代码所示。
public class Facade {
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();
//提供给外部访问的方法
public void methodA(){
this.a.doSomethingA();
}
public void methodB(){
this.b.doSomethingB();
}
public void methodC(){
this.a.doSomethingA();
this.c.doSomethingC();
}
}
还是非常简单,只是在methodC方法中增加了doSomethingA()方法的调用,可以这样做吗?我相信大部分读者都说可以这样做,而且已经在实际系统开发中这样使用了,我今天告 诉各位,这样设计是非常不靠谱的,为什么呢?因为你已经让门面对象参与了业务逻辑,门 面对象只是提供一个访问子系统的一个路径而已,它不应该也不能参与具体的业务逻辑,否则就会产生一个倒依赖的问题:子系统必须依赖门面才能被访问,这是设计上一个严重错 误,不仅违反了单一职责原则,同时也破坏了系统的封装性。
说了这么多,那对于这种情况该怎么处理呢?建立一个封装类,封装完毕后提供给门面对象。我们先建立一个封装类,如代码所示:
public class Context {
//委托处理
private ClassA a = new ClassA();
private ClassC c = new ClassC();
//复杂的计算
public void complexMethod(){
this.a.doSomethingA();
this.c.doSomethingC();
}
}
该封装类的作用就是产生一个业务规则complexMethod,并且它的生存环境是在子系统内,仅仅依赖两个相关的对象,门面对象通过对它的访问完成一个复杂的业务逻辑,如代码所示。
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();
}
}
通过这样一次封装后,门面对象又不参与业务逻辑了,在门面模式中,门面角色应该是稳定,它不应该经常变化,一个系统一旦投入运行它就不应该被改变,它是一个系统对外的 接口,你变来变去还怎么保证其他模块的稳定运行呢?但是,业务逻辑是会经常变化的,我 们已经把它的变化封装在子系统内部,无论你如何变化,对外界的访问者来说,都还是同一个门面,同样的方法——这才是架构师最希望看到的结构。

四总结

门面模式是一个很好的封装方法,一个子系统比较复杂时,比如算法或者业务比较复 杂,就可以封装出一个或多个门面出来,项目的结构简单,而且扩展性非常好。还有,对于 一个较大项目,为了避免人员带来的风险,也可以使用门面模式,技术水平比较差的成员, 尽量安排独立的模块,然后把他写的程序封装到一个门面里,尽量让其他项目成员不用看到 这些人的代码,看也看不懂,我也遇到过一个“高人”写的代码,private方法、构造函数、常 量基本都不用,你要一个public方法,好,一个类里就一个public方法,所有代码都在里面, 然后你就看吧,一大坨程序,看着就能把人逼疯。使用门面模式后,对门面进行单元测试, 约束项目成员的代码质量,对项目整体质量的提升也是一个比较好的帮助。

你可能感兴趣的:(java,设计模式,门面模式)