1. 外观模式
1.1 概述
外观模式比较简单,是应用比较广泛的一种设计模式。
举个栗子说明一下,比如说,现在我在家炒菜,我需要准备菜、调料、油等,然后再下锅炒,最后完成。但是,如果我们去饭店吃,只需要跟服务员说一下菜名,服务员就会为我们提供一份美味佳肴。本来如此繁复的一个工作,现在有了服务员的加入而变得简单,我们不需要和菜、调料等直接接触,也不需要了解具体的细节,就得到了我们想要的结果,这就应用到了今天要说到的模式——外观模式。
1.2 定义
外观模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。(引自《大话设计模式》)
1.3 结构图
这里主要涉及三个角色:
- 外观角色(Facade):外观模式的核心。对外为客户提供一组可用的接口,对内熟悉每一个子系统的功能,并根据客户需求对子系统的功能进行组合。
- 子系统角色(SubSystem):一个应用中可存在一个或多个子系统类,实现子系统功能,被外观角色所调用,处理Facade指派的功能。值得注意的是,子系统类中没有Facade的任何信息,即没有对Facade对象的引用。
- 客户角色(Client):通过调用Facade完成需要实现的功能。
2. 实战应用
2.1 实例说明
简单举个例子说明。现在有一个网站要做登录模块,登录的信息有用户名、密码和图片验证码,忽略前台需要做的一些相关规则的正则校验,只考虑后端服务端的校验。那么,有如下步骤:
(1)图片验证码校验。
(2)用户是否存在。
(3)密码校验。
由于这三个步骤的功能是相互独立的,而且也有可能被其他模块所调用,也考虑到让设计符合单一职责,所以可以将这三部分写到三个类中。
2.2 类图
2.3 实现代码
子系统一
package main.mode.wgms;
public class PictureCode {
public void CheckPictureCode() {
System.out.println("校验图片验证码");
}
}
子系统二
package main.mode.wgms;
public class UserInfo {
public void checkUserName() {
System.out.println("校验用户名是否存在");
}
}
子系统三
package main.mode.wgms;
public class PwdInfo {
public void checkPwd() {
System.out.println("校验用户名密码是否匹配");
}
}
外观类
package main.mode.wgms;
public class LoginFacade {
private PictureCode code;
private UserInfo user;
private PwdInfo pwd;
public LoginFacade() {
code = new PictureCode();
user = new UserInfo();
pwd = new PwdInfo();
}
public void login(){
System.out.println("进入登录功能......");
code.CheckPictureCode();
user.checkUserName();
pwd.checkPwd();
System.out.println("登录成功......");
}
}
客户端类中调用Facade类
package main.mode.wgms;
public class Client {
public static void main(String[] args) {
LoginFacade login = new LoginFacade();
login.login();
}
}
结果
2.4 分析
(1)子系统之间相互独立,完成各自的功能。
(2)外观类按客户需求组合子系统的功能,完成客户所需功能。
(3)从客户端代码可以看出,客户不需要知道登录具体做了哪些操作,即不知道PictureCode、UserInfo、PwdInfo的存在,只需要调用LoginFacade完成登录功能即可。客户端不需要知道系统内部的组成,不需要了解内部实现的细节,使得客户端和子系统类之间解耦。
3. 总结
3.1 优点
- 外观模式简单易用,完美地体现了依赖倒转原则和迪米特法则的思想。
- 使客户端和子系统之间解耦,提高了子系统的易扩展性和复用性。
- 客户端代码很简单。
3.2 缺点
- 外观类违背了开放-封闭原则,增加新的子系统需要修改外观类的代码。
3.3 应用场景
- 在分层架构中,可以通过外观类建立层与层之间的联系,减少层之间的联系,降低耦合性。
- 客户端与子系统联系过多时,或者子系统需要不断的重构或修改时,可以增加外观类,将客户端与子系统解耦,增加子系统的可扩展性和复用性。
- 当需要访问一个复杂或难以维护和扩展的遗留系统时,可以引入外观类,让外观类与遗留代码做交互。