外观模式 门面模式 Facade 结构型 设计模式(十三)

外观模式(FACADE)
又称为门面模式
外观模式 门面模式 Facade  结构型 设计模式(十三)
 

意图

为子系统中的一组接口提供一个一致的界面
Facade模式定义了一个高层接口,这一接口使得这一子系统更加易于使用。

意图解析

随着项目的持续发展,系统基本上都是会往功能更全面的方向发展,那么也就意味着我们的系统将会变得更加复杂。
系统会被划分为多个单独的子系统,每个子系统完成一部分功能,通过分工协作完成全部功能。
一个子系统也可能进一步拆分为更小的几个子系统。
 
程序中的文件将会越来越多,相互关联也会变得更加复杂
当使用一个功能的时候,作为客户端
你需要弄清楚相关类之间的关系,以及正确的调用顺序。
 
比如下图中
你需要自己识别有哪些子系统,涉及哪些相关的类和方法,你需要自己保证顺序(如果功能调用依赖顺序的话)
外观模式 门面模式 Facade  结构型 设计模式(十三)
 
如同在医院里面,病人需要自己去排队挂号化验,跟每个流程的工作人员进行协作
如同在工厂里面,需要生产一个桌子,你亲自用机器生产桌子腿,自己使用机器生产桌面...
如同你去其他公司洽谈业务,你单独跟每个相关业务的人员进行联系沟通
 
你肯定想得到,如果有一个中间人为你代劳
不需要面对林林总总的子系统、部门、人员...
当你需要某种服务时,只需要告诉这个中间人就好了
这个想法就是外观模式
外观模式 门面模式 Facade  结构型 设计模式(十三)
 
有了facade,你就不需要跟每个子系统进行单独的交流了
如同在医院里面,对于VIP,有专人代替你挂号.....
如同在工厂里面,有一个控制台机器,你选择产品,控制台下发命令安排其他的机器生产具体产品
如同你去其他公司洽谈业务,有一个接口人负责与你对接,他们那边的事情都通过这个人进行安排
 
外观模式 门面模式 Facade  结构型 设计模式(十三)
 
外观模式的意图含义,如同他的名字一样,“建筑物的正面”
面对一个复杂的大楼,当你在正面远远望去,也就只能看到正面
在外观模式中,形容一个庞大的复杂的系统的一个直观的界面
借助于Facade模式
从原来的“客户端需要跟多个子系统进行交互”,转变为“只与Facade进行交互”
将客户端与子系统进行解耦,降低了耦合性,也降低了使用的复杂度

代码示例

“关好门窗,防火防盗”这句话有没有听过?
回想一下,当你早上准备出门离开家时,你会做什么?
假设你会关水、关灯、关门窗。
我们创建三个类,水 灯 窗,模拟离开家的场景  
package facade;
public class Water {
public void turnOn() {
        System.out.println("打开水龙头...");
    }   
    public void turnOff() {
        System.out.println("关闭水龙头...");
    }
}
package facade;
public class Light {
    public void turnOn() {
        System.out.println("开灯...");
    } 
    public void turnOff() {
        System.out.println("关灯...");
    }
}
package facade;
 
public class Window {
    public void open() {
        System.out.println("开窗...");
    } 
    public void close() {
        System.out.println("关窗...");
    }
}
测试代码
外观模式 门面模式 Facade  结构型 设计模式(十三)
 
上面的测试代码Test作为客户端程序,可以看得出来,他直接跟Water Light Window三个类的对象进行交互
他需要调用相关的方法
也就是说Test 作为客户端对于“离家”这一系统的内部逻辑是了如指掌的--->需要断水、关灯、关窗
他也清楚每个类的方法
一方面增加了耦合性,另一方面将子系统的内部细节暴露出来

优化重构

试想下,如果你家是智能家居,有一个控制台Facade,或者说有一个手机App
他可以控制整个家庭的设备
package facade;
 
public class Facade {
    private Water water = new Water();
    private Light light = new Light();
    private Window window = new Window();
    
    public void leaveHome(){
        water.turnOff();
        light.turnOff();
        window.close();
    }
     
    public void backHome(){
        light.turnOn();
        window.open();
    }
 
}
外观模式 门面模式 Facade  结构型 设计模式(十三)
 
通过这个控制台,客户端程序不再需要了解子系统的内部细节,他也不清楚每个类到底有哪些方法
所有的交互都是通过Facade来完成的

结构

外观模式 门面模式 Facade  结构型 设计模式(十三)
Facade 外观角色
客户端调用角色,知晓子系统的所有功能与职责
通常,Facade会将所有的请求转发委派到子系统中去,也就是说该角色没有实际的业务、
 
SubSystem子系统角色
可以同时有一个或者多个子系统
注意   :子系统并不是说一个单独的类,而是一个类的集合,这些类根据逻辑功能点被组织在一起  
子系统并不知道Facade的存在,对于子系统来说,Facade也就只是一个客户端程序
 
外观模式的结构比较简单,类似一个“封装”提取的过程
他的根本原则为迪米特法则,也就是“不要和陌生人说话”,尽可能少的与其他的对象进行交互
通过外观模式,做到了子系统只与外观对象交互
 
门面类个数
在门面模式中,通常只需要一个门面类,并且这个门面类只有一个实例
换句话说他很可能是一个单例
但是并不是说整个系统中只能有一个门面类
门面类的个数要根据系统中子系统的个数以及业务逻辑的情况

总结

当你需要为一个复杂的子系统提供一个简单的接口时或者希望子系统能够更加独立时,可以考虑使用外观模式
借助于外观模式,可以实现客户端与子系统的解耦,减少客户端对子系统的依赖性
一旦完成解耦,就意味着子系统有良好的独立性,也能拥有更好的扩展性
因为独立了,就意味着单独的子系统修改不会影响其他系统
而且,在多层次结构的系统中,可以使用Facade模式进行层与层之间的交互,将层与层之间的耦合性降低,使他们仅仅通过facade进行交互
总之一句话,降低了使用子系统的复杂程度,降低了耦合程度,满足迪米特法则----不要和陌生人说话 
对客户端屏蔽了子系统的组件
仅仅通过Facade,大大减少了客户端所需要处理的对象的数目
对于外观模式,如果是子系统发生变化,Facade则极有可能需要面临修改,这不符合开闭原则 
外观模式(门面模式)就如同我们开篇的图片一样,作为公司前台
接待来访宾客,一切事宜都有她来协调安排组织。
在OOP中,这个“前台”不仅是一个子系统的“正面”看到的样子,而且还强调了她的全权负责
她提供所有的业务需要的相关方法,尽管内部调用都是子系统中的方法,她提供简单一致的交流沟通形式 
理解了迪米特法则,那么就比较容易理解外观模式
外观模式重点在于提供一个“简化”“封装”后的操作控制台,让你更容易操作整个系统,他几乎不会涉及子系统的内部逻辑
否则,门面对象将与子系统的业务逻辑耦合,增加了耦合度。