外观模式在日常开发过程中使用频率非常高,在组件化、模块化、SDK开发中更是常见。它是对外提供一个统一的高级接口,也就是外观类,而具体的业务细节都由外观类调用具体的业务模块来实现。
定义
在有多个子系统(模块)系统中提供一个高层次的接口,使得子系统更容易使用。
场景
- 为一个复杂的系统提供一个简单的接口
- 当需要构建一个层次结构的子系统时,使用外观模式来定义每个子系统的入口点,通过外观类来在子系统间进行通信,简化子系统之间的依赖关系
角色
外观类Facade
系统对外的统一接口
子系统
具体的子系统
代码说明
外观模式在SDK开发中会经常遇到,SDK提供一个入口类,入口类中通过初始化、业务开始等方法来启动SDK内部流程,这个入口类就是一个外观类,他是SDK的对外提供的一个高级接口,在该入口类中,会具体的调用各个业务模块的相关逻辑。比如我们有一台手机,他可以看视频、打游戏、打电话等。对于使用者,他其实只知道有个手机,这台手机可以提供三个功能,至于这三个功能具体是由谁实现的,使用者是不知道的。
针对这个场景,我们可以建立外观类
/**
* 外观类,提供了对外的统一接口
*
* 会调用具体的实现类来完成调用流程
*/
public class MobilePhone {
private Game game;
private Phone phone;
private Video video;
/**
* 初始化
*/
public void init() {
game = new Game();
phone = new Phone();
video = new Video();
}
/**
* 对外打游戏
* 实际上是调用Game的play方法
*/
public void playGame() {
this.game.play();
}
/**
* 对外打电话
* 实际上是调用Phone的dial方法
*/
public void dial() {
this.phone.dial();
}
/**
* 对外播放视频
* 实际上是调用Video的play方法
*/
public void playVideo() {
this.video.play();
}
}
在这个系统中,我们建立了三个子系统,分别为游戏、电话、视频
/**
* 游戏子系统
*/
public class Game {
void play() {
System.out.println("开始打游戏");
}
}
/**
* 视频子系统
*/
public class Video {
void play() {
System.out.println("开始看视频");
}
}
/**
* 电话子系统
*/
public class Phone {
void dial() {
System.out.println("开始打电话");
}
}
通过外观类,对外提供了统一的接口API,隐藏了具体业务类,并且使得各个业务类之间无耦合。
具体调用则是通过外观类来实现
public static void main(String[] args) {
MobilePhone mp = new MobilePhone();
mp.init();
mp.playGame();
mp.playVideo();
mp.dial();
}
产生的输出为
开始打游戏
开始看视频
开始打电话
从项目结构看,其实项目中包含四个类文件
MobilePhone
Game
Phone
Video
然而对于使用者,其实只知道外观类MobilePhone
,其他业务类对于使用者被完全隐藏了。
关于源码
安卓源码中也可以找到外观模式的影子,上下文对象Context
具有很多方法,比如获取资源管理对象、启动Activity、获取广播、获取内容提供者等等,而这些功能的具体调用者是ContextImpl
的对象,实际上ContextImpl
就是一个外观者对象,它在内部分别调用具体对象的对应方法来完成系统指定的功能
总结
外观模式使用的频率非常高,通过封装,对外提供一个高层次的统一接口,降低了使用成本。
当然外观模式还隐藏了子系统实现细节,减少客户对于子系统的耦合。