1. 外观设计模式介绍
显示生活中有一个种电视万能遥控器,只要和电视配对好了以后,就可以正常使用,不同型号的电视,只要一旦适配,所有的操作模式一模一样。
这就是一种外观适配模式。表面上都是同一个遥控器,实际上不同型号的电视,不同的操作,发出的型号可能各不相同。但是对于用户来说,没有任何差别。
定义
要求一个子系统的外部和其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易使用。
2. 外观设计模式使用场景
- 为一个复杂的子系统提供简单、固定的接口。比如我们常用的加载图片的框架,随着项目的推进,框架没有人维护,或者出现了效率更高的框架出现,此时如果项目进行更换框架,可能需要修改很多处。如果,项目特别大,更是灾难。但是如果我们之前采用了外观设计模式,设计了统一的封装接口,我们只需要接口里面详细的逻辑即可,而不必修改很多出。从外观就好像没有修改一样。
- 当构建一个层次结构的子系统时,使用外观设计模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,那么子系统之间可以通过外观设计模式定义的接口进行通讯,简化子系统之间的依赖关系。
3. 外观设计模式UML类图
4. 外观设计模式简单实例
现在以一个情形:一个子系统拥有三个模块,每个模块都有三个方法,其中一个为客户端调用方法,其它两个为各个子模块间互相调用方法。此时客户端需要组合三个模块中的方法才能完成功能。
- (1)、A、B、C三个模块方法:
A 模块方法接口:
public interface AModuleApi {
//此方法用于外部调用
public void a1();
//以下两个方法主要用于子系统内部间系统调用
public void a2();
public void a3();
}
A模块具体实现:
public class AModuleImpl implements AModuleApi {
@Override
public void a1() {
System.out.println("调用了A模块");
}
@Override
public void a2() {
//主要用于子模块间相互调用
}
@Override
public void a3() {
// 主要用于子模块间相互调用
}
}
以上是A模块中的方法,B、C两个模块和A一样。
- (2)、不采用外观设计模式,客户端如下:
public class Client {
public static void main(String[] args) {
AModuleApi aModule = new AModuleImpl();
aModule.a1();
BModuleApi bModule = new BModuleImpl();
bModule.b1();
CModuleImpl cModule = new CModuleImpl();
cModule.c1();
}
}
上面的代码我们会经常写,自习详细,会存在如下问题:
- 代码耦合度太高,客户端与子系统中各模块都有关联。一旦子系统有什么更改,会涉及到客户端的修改。
- 客户端想要知道每个模块中各个方法的含义才能进行调用。
现在采用了外观设计模式,添加一个外观类,由外观重组需要调用的方法
- (3)、添加一个外观类接口:
public interface FacadeApi {
public void a1();
public void b1();
public void c1();
public void test();
}
- (4)、外观类的具体实现:
public class FacadeImpl implements FacadeApi {
@Override
public void a1() {
new AModuleImpl().a1();
}
@Override
public void b1() {
new BModuleImpl().b1();
}
@Override
public void c1() {
new CModuleImpl().c1();
}
@Override
public void test() {
a1();
b1();
c1();
}
}
在test方法中,调用了三个模块中的方法。外观类是在服务端的,不是这客户端。客户端不需要知道具体的内部实现是什么样的。
- (5)、客户端:
public class Client {
public static void main(String[] args) {
/**
* 直接调用外观类里面的方法就可以了,内部细节不用知道,
* 计算改变了,也只需要调用这两行代码即可。
*/
FacadeApi facade = new FacadeImpl();
facade.test();
}
}
客户端只需要调用外观模式中的代码即可,不需要关注具体实现细节。
5. 外观设计模式在Android源码
我们经常在Activity中调用其他的方法,比如,Activity的跳转,发送广播,启动服务等。这些方法都是封装在Context类中,Context是一个抽象类,具体实现是ContextImpl。
ContextImpl内部封装了很多不同子系统的操作,上面说的这些操作就是在这里面,然后它的具体实现并不在ContextImpl里面,而是在各个不同的子系统进行处理。
这张图就是ContextImpl和各个子类系统之间的调用关系:
6. 外观设计模式在Android开发中
在开发中,我们经常会用到图片加载,网络加载框架。有时候,随着时间的推移,我们所使用框架的作者有可能不再维护,或者出现了效率更高的新的框架。
这时如果我们想要修改为新的框架,可能比较困难,可能太多的地方用到了。
但是如果我们采用了外观设计模式,用了一个外观类来包装,调用的时候直接调用外观方法,而不是直接调用。以后在需要修改的时候,只需要修改包装类的方法即可。
实例:
public class ImageLoaer {
public Image loadImage(String url, int width, int height) {
//调用具体的图片框架方法加载
}
}
7. 总结
- 优点:
- 对客户端程序隐藏子系统细节,减少了客户端对子系统的耦合。
- 外观类对子系统的封装,使得系统更易于使用。