门面模式【Facade Pattern】也叫外观模式,是一种比较常用的封装模式,其定义如下:
【要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更容易使用】
门面模式注重“统一的对象”,也就是提供了一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生。
门面模式的系统类图如下:
类图就是这么简单,但是意义其实很复杂
Subsystem Classes 是所有子系统所有类的简称,它可能代表一个类,也可能代表几十个对象的集合。不管多少对象,我们都将他们化为子系统的范畴,结构如图:
【不管子系统有多麻烦,外部只能通过这个【Facade——门面对象】来进行通信】
一般情况下,Facade会将所有从客户端发来的请求委派到相应的子系统去,也就是说该角色没有实际的 业务逻辑,是一个委托类。
下面是门面模式的通用源码
//子系统源代码
public class ClassA{public void doSomethingA(){//业务逻辑}
}
public class ClassB{public void doSomethingB(){//业务逻辑}
}
public class ClassC{public void doSomethingC(){//业务逻辑}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
//门面对象
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.c.doSomethingC();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
下面用一个例子来看一下具体的门面模式
例子:【我要投递信件】
我们都写过纸质的信件,比如给女朋友写情书什么的。写信的过程大家应该都记得——先写信的内容,然后写信封,在把信放到信封里,封好,投递到信箱中进行投递。虽然简单,但是这四个步骤都是不可或缺的!
首先我们看如果不使用门面模式应该是怎样实现的
public interface ILetterProcess {//首先要写信的内容public void writeContext(String context);//其次写信封public void fillEnvelope(String address);//然后邮递public void letterInotoEnvolope();//然后邮递public void sendLetter();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
public class LetterProcessImpl implements ILetterProcess {@Overridepublic void writeContext(String context) {System.out.println("填写信的内容... " + context);}@Overridepublic void fillEnvelope(String address) {System.out.println("填写收信人的姓名和地址..." + address);}@Overridepublic void letterInotoEnvolope() {System.out.println("把信放到信封中...");}@Overridepublic void sendLetter() {System.out.println("邮递信件...");}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
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 your old lover.");letterProcess.fillEnvelope("Happy Road No. 666, God Province, Heaven"); letterProcess.letterInotoEnvolope();letterProcess.sendLetter();}
}
Output:
/**
填写信的内容... Hello, It's me, do you know Who I am? I'm your old lover.
填写收信人的姓名和地址...Happy Road No. 666, God Province, Heaven
把信放到信封中...
邮递信件...*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
上面的代码与高内聚的要求相差甚远,更不说迪米特原则和接口隔离原则了。
怎么办呢?
邮局出了新的业务,你告诉我信的内容和地址,其余的我帮你搞定。
加了一个邮局的类
public class ModenPostOffice {//这就是门面对象private ILetterProcess letterProcess = new LetterProcessImpl();public void sendLetter(String context, String address){letterProcess.writeContext(context);letterProcess.fillEnvelope(address);letterProcess.letterInotoEnvolope();letterProcess.sendLetter();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
修改场景类
public class Client {public static void main(String[] args) {ModenPostOffice modenPostOffice = new ModenPostOffice();String context = "Hello, It's me, do you know Who I am? I'm your old lover.";String address = "Happy Road No. 666, God Province, Heaven";modenPostOffice.sendLetter(context, address);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
//Output
/**
填写信的内容... Hello, It's me, do you know Who I am? I'm your old lover.
填写收信人的姓名和地址...Happy Road No. 666, God Province, Heaven
把信放到信封中...
邮递信件...
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
下面我们要通过一个要求来看一下门面模式的优点:
要求:【新建发送之前请先检查信件是否安全】
//增加了检查信件的Police类
public class Police {public void checkletter(ILetterProcess letterProcess){System.out.println(letterProcess + " 信件已经被检查过了...");}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
//修改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.letterInotoEnvolope();letterProcess.sendLetter();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
场景类完全一样
//Output
/**
填写信的内容... Hello, It's me, do you know Who I am? I'm your old lover.
填写收信人的姓名和地址...Happy Road No. 666, God Province, Heaven
com.sdkd.LetterProcessImpl@74a14482 信件已经被检查过了...
把信放到信封中...
邮递信件...
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
可以看到这个新的要求对客户是透明的,他什么都不用管,只是负责告诉邮局内容和地址就可以了。
不改变子系统对外暴露的接口 、方法,只改变内部的处理逻辑,其他兄弟模块的调用产生了想要得到结果,这确实是一个非常好的设计。这就是门面模式。
最后的UML图如下,层次模块很明显
门面模式的优点:
1、减少系统的相互依赖
2、提供了灵活性
3、提供了安全性
门面模式的缺点:
门面模式不符合开闭原则,一旦出错风险很大。
门面模式的使用场景:
1、为一个复杂的模块或子系统提供一个供外界访问的接口
2、子系统相对独立,对外界来说访问只要暗箱操作就可以
3、预防低水平人员带来的风险扩散。