定义
外观模式(Facade Pattern)属于结构型模式,其为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
光看定义有点枯燥,我来举个例子:
假设我们去一个餐厅吃饭,服务员写下了我们点的菜单,然后通知厨师按照订单来做菜,厨师做好后就通知服务员,然后服务员将饭菜给我们端上来,我们吃好后,清洁员来收拾餐桌并洗碗筷。 如果我们要实现这个场景,按照情景我们画一下这个类图:
看图应该很容易理解:客户端通过调用各个子系统类的方法来实现顾客用餐的全过程,但是问题也来了,这样设计不仅会造成耦合性过高而且子系统在开发阶段往往会因为不断地重构演化而变得越来越复杂,给客户端的调用造成麻烦。因此我们引进外观模式来解决这一问题,通过将子系统中类的方法提取出来放在一个中间层类中,由他来控制子系统中方法的调用,而客户端只需要调用这个中间类即可。
模式结构图
说明:
- 外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
- 子系统的集合(SubSystem):指的是模块或者子系统,处理Facade对象指派的任务,它是功能的世纪提供者
- 客户端(Client):外观类的调用者
实现
我们以上述顾客就餐为例来编写代码实现外观模式:
先写一个服务员类Waiter:
package com.design_pattern.facade;
/**
* Created on 2020/3/21
* Package com.design_pattern.facade
*
* @author dsy
*/
public class Waiter {
//用了单例模式
private static Waiter instance = new Waiter();
public static Waiter getInstance(){
return instance;
}
public void writeOrder(){
System.out.println("服务员写下顾客点的菜品");
}
public void notifyCook(){
System.out.println("服务员通知厨师");
}
public void serveCustomer(){
System.out.println("服务员为顾客端菜");
}
public void cleanUp(){
System.out.println("服务员收拾餐桌");
}
}
关于对象的创建我们采用了单例模式,不懂的朋友可以看这篇文章
然后再写一个厨师类Cook:
package com.design_pattern.facade;
/**
* Created on 2020/3/21
* Package com.design_pattern.facade
*
* @author dsy
*/
public class Cook {
private static Cook instance = new Cook();
public static Cook getInstance(){
return instance;
}
public void prepareFood(){
System.out.println("厨师准备菜品");
}
public void notifyWaiter(){
System.out.println("厨师通知服务员");
}
}
还有清洁员类DishWasher :
package com.design_pattern.facade;
/**
* Created on 2020/3/21
* Package com.design_pattern.facade
*
* @author dsy
*/
public class DishWasher {
private static DishWasher instance = new DishWasher();
public static DishWasher getInstance() {
return instance;
}
public void washDishes() {
System.out.println("清洁员洗刷碗筷");
}
}
然后我们来看Facade类:
package com.design_pattern.facade;
/**
* Created on 2020/3/21
* Package com.design_pattern.facade
*
* @author dsy
*/
public class Facade {
private Waiter waiter;
private Cook cook;
private DishWasher dishWasher;
public Facade(){
this.waiter = Waiter.getInstance();
this.cook = Cook.getInstance();
this.dishWasher = DishWasher.getInstance();
}
public void orderFood(){
waiter.writeOrder();
waiter.notifyCook();
cook.prepareFood();
cook.notifyWaiter();
waiter.serveCustomer();
}
public void leave(){
waiter.cleanUp();
dishWasher.washDishes();
}
}
最后客户端Client调用:
package com.design_pattern.facade;
/**
* Created on 2020/3/21
* Package com.design_pattern.facade
*
* @author dsy
*/
public class Client {
public static void main(String[] args) {
//创建一个Facade对象
Facade facade = new Facade();
//顾客点餐
facade.orderFood();
//顾客吃完离开了
facade.leave();
}
}
打印输出:
服务员写下顾客点的菜品
服务员通知厨师
厨师准备菜品
厨师通知服务员
服务员为顾客端菜
服务员收拾餐桌
清洁员洗刷碗筷
总结
使用外观模式的好处
- Facade定义了一个更高级别的接口,该接口通过包装复杂的子系统使子系统更易于使用。
- 由于Facade的作用,客户端可以完全不知道子系统类的存在。
- 你可以让自己的代码独立于复杂子系统。
何时使用外观模式
- 你想要封装或隐藏原始系统
- 编写新类的成本低于学习原始子系统类的成本,也就是减少了成功利用子系统所需的学习曲线
- Facade定义了一个更高级别的接口,该接口通过包装复杂的子系统使子系统更易于使用。