门面模式
例子
写信寄信。只传递必要内容,具体执行顺序由邮局统一处理。通过在邮局内创建警察对邮件进行检查,完全隔离客户。
public interface ILetterProcess {
void writeContext(String context);
void fillEnvelope(String address);
void letterIntoEnvelope();
public void sendLetter();
}
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 letterIntoEnvelope() {
System.out.println("把信放到信封中...");
}
@Override
public void sendLetter() {
System.out.println("邮递信件...");
}
}
public class ModenPostOffice {
private final ILetterProcess letterProcess = new LetterProcessImpl();
private final Police letterPolice = new Police();
public void sendLetter(String context, String address) {
letterProcess.writeContext(context);
letterProcess.fillEnvelope(address);
letterPolice.checkLetter(letterProcess);
letterProcess.letterIntoEnvelope();
letterProcess.sendLetter();
}
}
public class Police {
public void checkLetter(ILetterProcess letterProcess){
System.out.println(letterProcess+" 信件已经检查过了...");
}
}
public class Client {
public static void main(String[] args) {
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 lover. I'd like to....";
hellRoadPostOffice.sendLetter(context, address);
}
}
定义
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
- Facade门面角色
客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下, 本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务 逻辑,只是一个委托类。 - subsystem子系统角色
可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集 合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。
public class ClassA {
public void doSomethingA(){
}
}
public class ClassB {
public void doSomethingB(){
}
}
public class ClassC {
public void 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 c = new Context();
public void methodA() {
this.a.doSomethingA();
}
public void methodB() {
this.b.doSomethingB();
}
public void methodC() {
this.c.complexMethod();
}
}
优点
- 减少系统的相互依赖
所有的依赖都是对门面对象的依赖,与子系统无关。 - 提高了灵活性
依赖减少了,灵活性自然提高了。不管子系统内部如何变化,只要不影响到门面对象, 任你自由活动。 - 提高安全性
想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到。
缺点
门面模式最大的缺点就是不符合开闭原则。一旦在系统投产后发现有一个小错误,唯一能做的一件事就是修改门面角色的代码,这个风险相当大,这就需要大家在设计的时候慎之又慎,多思考几遍才会有好收获。
使用场景
- 为一个复杂的模块或子系统提供一个供外界访问的接口
- 子系统相对独立——外界对子系统的访问只要黑箱操作即可
比如利息的计算问题,没有深厚的业务知识和扎实的技术水平是不可能开发出该子系统 的,但是对于使用该系统的开发人员来说,他需要做的就是输入金额以及存期,其他的都不用关心,返回的结果就是利息,这时候,门面模式是非使用不可了。 - 预防低水平人员带来的风险扩散
比如一个低水平的技术人员参与项目开发,为降低个人代码质量对整体项目的影响风险,一般的做法是“画地为牢”,只能在指定的子系统中开发,然后再提供门面接口进行访问操作。
如果需要创建新的门面,直接在原有门面中调用,不再编写一个委托到子系统的方法,避免以后到处修改相似代码的悲剧。
public class Facade2 {
private Facade facade = new Facade();
public void methodB(){ this.facade.methodB(); }
}
注意事项
一个子系统可以有多个门面
- 门面已经庞大到不能忍受的程度
一个纯洁的门面对象已经超过了200行的代码,虽然都是非常简单的委托操作,也 建议拆分成多个门面。按照 功能拆分是一个非常好的原则,比如一个数据库操作的门面可以拆分为查询门面、删除门面、更新门面等。 - 子系统可以提供不同访问路径
门面不参与子系统内的业务逻辑
门面模式不允许类内相互调用,即不允许业务纠缠。创建另外的类处理业务,添加到门面内。
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 c = new Context();
public void methodA() {
this.a.doSomethingA();
}
public void methodB() {
this.b.doSomethingB();
}
public void methodC() {
this.c.complexMethod();
}
}
最佳实践
门面模式是一个很好的封装方法,一个子系统比较复杂时,比如算法或者业务比较复 杂,就可以封装出一个或多个门面出来,项目的结构简单,而且扩展性非常好。