「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
1.定义
外部与一个子系统间的通信必须通过一个统一的外观对象进行,这个外观对象为子系统中的一组接口提供一个一致的界面,外观模式创建了一个高层接口,这个接口使得子系统更加容易使用。
2.模式结构
3.时序图
4.代码分析
代码案例:以我家中的小爱为例,下班后我要打开热水器、空调、饮水机,上班后我要关闭热水器、空调、饮水机,如果不采用外观模式(小爱同学)的方式就需要我自己去执行每一个电器的关闭操作,使用小爱同学后,我只需要告诉它就可以帮我做完那些事情,具体是怎么执行的我并不知道也不需要知道。
案例中有两个角色
- 外观模式的包装类:小爱同学
- 子系统:热水器、空调、饮水机
/**
* 小爱同学
*/
public static class XiaoAiSchoolmate {
ControlConcrete concrete;
ControlAirConditioner airConditioner;
ControlWaterDispenser waterDispenser;
public XiaoAiSchoolmate(ControlConcrete concrete, ControlAirConditioner airConditioner, ControlWaterDispenser waterDispenser) {
this.concrete = concrete;
this.airConditioner = airConditioner;
this.waterDispenser = waterDispenser;
}
public void home() {
System.out.println("欢迎回家");
concrete.open();
airConditioner.open();
waterDispenser.open();
}
public void work() {
System.out.println("好的,工作愉快");
concrete.close();
airConditioner.close();
waterDispenser.close();
}
}
/**
* 控制热水器
*/
public static class ControlConcrete {
public void open() {
System.out.println("打开热水器");
}
public void close() {
System.out.println("关闭热水器");
}
}
/**
* 控制空调
*/
public static class ControlAirConditioner {
public void open() {
System.out.println("打开空调");
}
public void close() {
System.out.println("关闭空调");
}
}
/**
* 控制饮水机
*/
public static class ControlWaterDispenser {
public void open() {
System.out.println("打开饮水机");
}
public void close() {
System.out.println("关闭饮水机");
}
}
/**
* 具体使用
*/
public static void main(String[] args) {
ControlConcrete concrete = new ControlConcrete();
ControlAirConditioner airConditioner = new ControlAirConditioner();
ControlWaterDispenser waterDispenser = new ControlWaterDispenser();
XiaoAiSchoolmate schoolmate = new XiaoAiSchoolmate(concrete, airConditioner, waterDispenser);
System.out.println("小爱同学,我到家了");
schoolmate.home();
System.out.println("小爱同学,我去上班了");
schoolmate.work();
}
执行结果:
小爱同学,我到家了
欢迎回家
打开热水器
打开空调
打开饮水机
小爱同学,我去上班了
好的,工作愉快
关闭热水器
关闭空调
关闭饮水机
5.模式分析
根据【单一职责原则】在软件中将一个系统划分为若干个子系统,使得子系统之间的通信和依赖性降低,而达到该目标的途径之一就是引入一个外观类,它为子系统提供一个简单且单一的入口。外观模式主要的目的就是通过外观类把客户端与子系统隔离开,这样就降低了客户端使用时的复杂度。
6.优点
- 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台直接的移植性,因为编译一个子系统时不用编译其他子系统,一个子系统的修改对其他子系统也没有影响,而且子系统内部有变化时也不会影响到外观对象;
- 降低了与客户端的耦合性,子系统的修改只需要修改外观对象即可;
- 降低了客户端调用的复杂性,客户端无需知道有哪些子系统,只需要调用外观对象中的方法即可。
- 只是提供了一个统一访问子系统的入口而已,客户端也可以单独调用某个子系统。
7.缺点
- 如果对客户访问子系统类做了过多的限制则减少了可变性和灵活性;
- 如果没有引入抽象外观类那么在增加一个新的子系统的时候就要修改外观类和客户端的代码,违背了开闭原则。
8.适用环境
- 在层次化的结构中,可以使用外观模式定义系统中每一层的入口,这样可以保证层与层之间是没有关系的,而通过外观类建立可以降低层之间的耦合度;
- 要为一个复杂的子系统提供一个简单的入口时可以使用外观模式。该接口可以满足大多数用户的需求,并且客户也可以越过外观类直接调用子系统;
- 客户端与多个子系统之间都有关联,可以通过外观模式将其封装,这样也可以提高可移植性。
9.扩展
- 一个系统有多个外观类
系统中可以存在多个外观了,在不同的外观类中引入不同的子系统以实现不同的功能。
- 不要试图通过外观类为子系统增加新行为
外观类的存在就是为了将子系统包装起来便于客户端调用并且解耦,利用外观类为子系统增加新的行为显然违背了最初的目的,如果要增加新的行为可通过子系统实现。
- 抽象外观类的引入
在原有的外观模式下如果要增加或移除一个子新的系统就需要修改外观对象和客户端调用的的代码,这违背了开闭原则,如果引入抽象外观类就可以让具体的外观类来关联新的子系统,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。
参考链接:外观模式