外观模式(Facade Pattern):又称门面模式。外部通过一个统一的接口,访问子系统中的一群接口。外观模式定义了一个高层接口,为子系统中的一组接口提供了一个一致的入口,使得子系统更容易使用。外观模式是一种对象结构型模式
外观模式结构中,包含两个角色,外观角色和子系统角色
外观角色(Facade):客户端调用这个角色,通过外观角色将客户端的请求委派到相关的子系统中
子系统角色(SubSystem):子系统角色可以有一个或多个,其中每一个子系统可以是单独的类或类的集合
外观模式,使客户类与子系统之间的通信和相互依赖关系降到最低,降低了系统的复杂度,很好的体现了迪米特法则
下面以买书为例讲解外观模式
我们想要买一本书,一般直接去书店或者联系出版社,很少有人会自己从排版、印刷、到装订来自己制作一本想要的书。我们想要一本书,不是自己一道道工序的去制作,而是通过出版社直接购买,将书籍制作的过程交给出版社,由出版社去排版、印刷、装订。这里,出版社就充当了外观角色,而排版、印刷、装订分别为子系统角色
下边代码演示外观模式
定义排版服务
package com.design.structural.facade;
/**
* 排版服务
*/
public class TypesetService {
public void typesetBook(){
System.out.println("书籍排版完成");
}
}
定义印刷服务
package com.design.structural.facade;
/**
* 印刷服务
*/
public class PrintService {
public void printBook(){
System.out.println("书籍印刷完成");
}
}
定义装订服务
package com.design.structural.facade;
/**
* 装订服务
*/
public class BindService {
public void bindBook(){
System.out.println("书籍装订完成");
}
}
定义出版社
package com.design.structural.facade;
public class PublisherService {
//排版服务
private TypesetService typesetService;
//印刷服务
private PrintService printService;
//装订服务
private BindService bindService;
public PublisherService(){
typesetService = new TypesetService();
printService = new PrintService();
bindService = new BindService();
}
//出版书籍
public void publishBook(){
typesetService.typesetBook();
printService.printBook();
bindService.bindBook();
}
}
类图如下
排版、印刷、装订三个子系统角色通过出版社这个外观角色统一进行调用
测试调用
package com.design.structural.facade;
public class TestMain {
public static void main(String[] args) {
PublisherService publisherService = new PublisherService();
publisherService.publishBook();
}
}
效果如下
扩展
(1)在标准外观模式中,增加或删除与外观类相关的子系统类时,必然要修改外观类或客户端的源代码,这违背了开闭原则,因此可以引入抽象类来对系统进行改进,引入抽象外观类,一旦业务变更,客户端可以通过更换具体外观类来完成,而不必修改源代码
(2)外观模式与单例模式。很多时候,为了节省系统资源,系统中只需要一个外观实例,因此外观实例可以是一个单例类。当将外观类设计成单例类时,它一定是一个具体外观类,而不是抽象外观类
外观模式总结
优点:简化调用过程,不需要了解子系统,防止带来风险;减少系统依赖、松散耦合;划分访问层次
缺点:增加子系统需要修改外观类源代码,违背了开闭原则
适用场景:子系统复杂,通过外观模式提供一个简单的入口;需要构建多层结构,降低层之间耦合度