本文主要介绍外观模式及其抽象外观的原理和实现。
模式背景
首先,使用外观模式之前就已经拥有一个很好的起点,系统业务中某些模块已经封装好了,而且职责单一。但是最终使用避免不了模块与模块,类与类之间的交互。这种交互该如何优化?就是外观模式要解决的。
书上举了一个例子,系统中有三个子模块:1.负责加密,2.负责读文件,3负责写文件。这三个模块各司其职,职能已经划分好了。但是使用使用的时候,我们会有大量的地方需要成批的调用者1-2-3的逻辑,每个地方都写这些逻辑毫无复用而言。所以我们应该将这些组合的单一职责逻辑封装到一个新类上,让外界统一通过这个新类来访问,这就是外观模式。
定义&概念
**外观模式(Facade Pattern):**为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。(外观模式又称为门面模式,是一种对象结构型模式)
原理
外观模式的目的是为了简化类于类之间的交互,其本质思想来源于封装。子系统是已经设计好的单一职责类,外观模式是将这些单一的职责聚合起来,提供一套模板。让需要使用这套模板的类,直接使用外观类进行访问。这样来提高代码的复用性。
必要条件
- Facade(外观角色)
- 客户端直接调用的那个外观,负责和其他多个子系统进行交互。
- SubSystem(子系统角色)
- 每个子系统分别有自己对应的功能,可以被客户端直接调用,也可以被外观角色调用。他即使客户端想要实现功能的具体办事人。
抽象外观模式
外观模式分为简单外观和抽象外观。简单外观就是对子模块添加外观类。但是这么会有一个问题,就是对子模块不可扩展。如果我们需要替换,增加,移除子模块,那么就需要去修改外观类,或者添加外观类去修改客户端代码,这违背了开闭原则。这时候可以针对外观类添加抽象层,来实现外观类的扩展,而客户端使用抽象外观进行编程。
UML
简单外观模式
抽象外观模式
实现
简单外观
直接将子系统的功能包装成一个外观类。
实现
子系统
//X: 0,1,2....N
public class SubSystemX {
public void op() {
System.out.println("system 0X is doing x ...");
}
}
外观
public class Facade {
private SubSystem01 s1;
private SubSystem02 s2;
public Facade(SubSystem01 s1, SubSystem02 s2) {
this.s1 = s1;
this.s2 = s2;
}
public void op() {
s1.op1();
s2.op2();
}
}
客户端
Facade facade = new Facade(new SubSystem01(),new SubSystem02());
facade.op();
抽象外观类
上面简单外观有个问题:如果想要修改外观类的实现,比如增加减少和外观类交互的子系统,那么就要修改该外观类以及客户端了。所以需要引入一个抽象,这样以后只需要添加新的外观类就可以了。客户端具体使用哪个外观类通过配置就可以搞定。
实现
public abstract class AbstractFacade {
public abstract void op();
}
public class FacadeNew extends AbstractFacade {
private SubSystem03 s3;
public FacadeNew(SubSystem03 s3) {
this.s3 = s3;
}
@Override
public void op() {
s1.op1();
}
}
//使用
AbstractFacade abstractFacade = new FacadeNew(new SubSystem01());
// 可以通过配置来加载具体的外观类
abstractFacade.op();
优缺点
优点
- 简化了调用过程,无须了解、深入子系统,防止带来风险。
- 减少系统依赖、松散耦合。
- 更好的划分访问层次,外观层次和下面子模块层次分开,还可以外观套外观。
- 符合迪米特法则,即最少知道原则。
缺点
- 没法很好的限制客户端直接使用子系统,这个缺点在某些情况才存在。
注意事项
- 为了节约系统资源,外观角色可以设计为一个单例类,并确保系统中只有唯一一个访问的入口。
- 一个系统可以设计多个外观类,分别提供不同的功能。
- 不要在外观类中对子系统的功能有添加,外观类只是集中这些功能,不是扩充这些功能。不要加入新的行为,如果新行为应该在子系统中添加,或者直接添加子系统。不要使用外观类来添加。
使用场景
这类场景还是比较多的。当你写代码的时候,在写某一块业务的时候,你发现写的过程中,这边需要调用A的东西,又需要调用B的东西,然后这样的代码在很多地方又都要用到,你就可以考虑使用外观模式。
总结
外观模式也是一个很简单的模式(其实很多时候我们已经这么用了,只是不知道官方话怎么讲)。这个模式主要就2个要点:
- 当我们需要将多个职责封装在一起的时候,使用外观模式提供一个统一的访问入口。
- 当外观模式中的子系统可能会有扩展和替换的时候,使用抽象外观去支持其扩展性。
附
如有代码和文章问题,还请指正!感谢!