写在前面的话
复习、总结23种设计模式
上一篇
门面模式(也叫外观模式)
记重点
提供一个类用于客户访问它来间接访问我们的内部业务,我们的内部细节对于客户来说是透明的
定义
定义:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。
门面模式提供一个高层次的接口,使得子系统更易于使用。
再简单地说,门面对象是外界访问子系统内部的唯一通道,不管子系统内部是多么杂乱无章,只要有门面对象在,就可以做到"金玉其外,败絮其中"。我们先明确一下门面模式的角色。
Facade门面角色
客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。
subsystem子系统角色
可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。
从写一封信来认识门面模式
我们都写过纸质信件吧,比如给女朋友写情书什么的。写信的过程大家应该都还记得一先写信的内容,然后写信封,再把信放到信封中,封好,投递到信箱中进行邮递,这个过程还是比较简单的,虽然简单,但是这4个步骤都不可或缺!我们先把这个过程通过程序实现出来如图所示

写信过程接口
java
package com.design.pattern.facade.test01;
public interface ILetterProcess {
// 首先要写信的内容
void writeContext(String context);
// 其次写信封
void fillEnvelope(String address);
// 把信放到信封里
void letterInotoEnvelope();
// 然后邮递
void sendLetter();
}
写信过程的实现
java
package com.design.pattern.facade.test01;
public class LetterProcessImpl implements ILetterProcess{
@Override
public void writeContext(String context) {
System.out.println("填写信的内容..." + context);
}
@Override
public void fillEnvelope(String address) {
System.out.println("填写收件人地址及姓名..." + address);
}
@Override
public void letterInotoEnvelope() {
System.out.println("把信放到信封里...");
}
@Override
public void sendLetter() {
System.out.println("寄出去...");
}
}
如果没有门面模式,什么事都要你亲力亲为,而且步骤还不能乱,要不然信发不出去的
在这种环境下,最累的是写信人,为了发送一封信要有4个步骤,而且这4个步骤还不能颠我们先看看这个过程如何通过程序表现出来,有人开始用这个过程写信了,
java
package com.design.pattern.facade.test01;
public class Client {
public static void main(String[] args) {
// 发出一封信需要用户依次调用以下逻辑
ILetterProcess letterProcess = new LetterProcessImpl();
letterProcess.writeContext("信的内容");
letterProcess.fillEnvelope("收件人联系方式和地址");
letterProcess.letterInotoEnvelope();
letterProcess.sendLetter();
}
}
我们回过头来看看这个过程,它与高内聚的要求相差甚远,更不要说迪米特法则、接口隔离原则了。你想想,你要知道这4个步骤,而且还要知道它们的顺序,一旦出错,信就不可能邮寄出去,这在面向对象的编程中是极度地不适合,它根本就没有完成一个类所具有的单一职责还有,如果信件多了就非常麻烦,每封信都要这样运转一遍,非得累死,更别说要发个广告信了,那怎么办呢?还好,现在邮局开发了一个新业务,你只要把信件的必要信息告诉我我给你发,我来完成这4个过程,只要把信件交给我就成了,其他就不要管了。非常好的方案!我们来看类图,如图所示。

现代化邮局
java
package com.design.pattern.facade.test02;
import com.design.pattern.facade.test01.ILetterProcess;
import com.design.pattern.facade.test01.LetterProcessImpl;
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();
}
}
这个类是什么意思呢,就是说现在有一个Hell Road PostOffice (地狱路邮局)提供了一种新型服务,客户只要把信的内容以及收信地址给他们,他们就会把信写好,封好,并发送出去。这种服务推出后大受欢迎,这多简单,客户减少了很多工作,谁不乐意呀。那我们看看客户是怎么调用的,
java
package com.design.pattern.facade.test02;
public class Client {
public static void main(String[] args) {
ModenPostOffice modenPostOffice = new ModenPostOffice();
modenPostOffice.sendLetter("信内容","收件人联系方式和地址");
}
}
运行结果是相同的。我们看看场景类是不是简化了很多,只要与ModenPostOffice交互就成了,其他的什么都不用管,写信封啦、写地址啦····都不用关心,只要把需要的信息提交过去就成了,邮局保证会按照我们指定的地址把指定的内容发送出去,这种方式不仅简单,而且扩展性还非常好,比如一个非常时期,寄往God Province (上帝省)的邮都必须进行安全检查那我们就很好处理了,如图所示。

增加了一个Police类,负责对信件进行检查
java
package com.design.pattern.facade.test03;
import com.design.pattern.facade.test01.ILetterProcess;
public class Police {
public void checkLetter(ILetterProcess letterProcess) {
System.out.println("检查信件...");
}
}
扩展后的现代化邮局:写信、封装、投递一体化了,当然也过审了
java
package com.design.pattern.facade.test03;
import com.design.pattern.facade.test01.ILetterProcess;
import com.design.pattern.facade.test01.LetterProcessImpl;
public class ModenPostOffice {
private ILetterProcess letterProcess = new LetterProcessImpl();
private Police police = new Police();
public void sendLetter(String context, String address) {
letterProcess.writeContext(context);
letterProcess.fillEnvelope(address);
police.checkLetter(letterProcess);
letterProcess.letterInotoEnvelope();
letterProcess.sendLetter();
}
}
只是增加了一个letterPolice变量的声明以及一个方法的调用,那这个写信的过程就变成这样:先写信、写信封,然后警察开始检查,之后才把信放到信封,最后发送出去,那这个变更对客户来说是透明的,他根本就看不到有人在检查他的邮件,他也不用了解,反正现代化的邮件系统都帮他做了,这也是他乐意的地方。
高层模块没有任何改动,但是信件却已经被检查过了。这正是我们设计所需要的模式,不改变子系统对外暴露的接口、方法,只改变内部的处理逻辑,其他兄弟模块的调用产生了不同的结果,确实是一个非常棒的设计。这就是门面模式。
门面模式的优点
减少系统的相互依赖
想想看,如果我们不使用门面模式,外界访问直接深入到子系统内部,相互之间是一种强糊合关系,你死我就死,你活我才能活,这样的强依赖是系统设计所不能接受的,门面模式的出现就很好地解决了该问题,所有的依赖都是对门面对象的依赖,与子系统无关。提高了灵活性 依赖减少了,灵活性自然提高了。不管子系统内部如何变化,只要不影响到门面对象,任你自由活动。
提高安全性
想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到
门面模式的缺点
门面模式最大的缺点就是不符合开闭原则,对修改关闭,对扩展开放,看看我们那个门面对象吧,它可是重中之重,一旦在系统投产后发现有一个小错误,你怎么解决?完全遵从开闭原则,根本没办法解决。继承?写?都顶不上用,唯一能做的一件事就是修改门面角色的代码,这个风险相当大,这就需要大家在设计的时候慎之又慎,多思考几遍才会有好收获。