外观模式
面对一个庞大且复杂的系统,我们很难去记住每个使用方法和它们之间的关系,我们需要一个东西帮我们简化整个系统,让我们能快速上手使用这个系统
概念
为子系统的一组接口提供一个统一的高层接口,在外部与一个子系统打交道只需要与一个对象交互即可。外观模式,又称门面模式,是 一种对象结构形模式
角色
- Facade
简单窗口,是向外部系统提供的高阶接口,比如Web后端接口 - SubSystem
子系统,在一个软件系统中可以有多个子系统,子系统不知道客户端的存在 - Client
客户端,使用Facade的角色,实际并不算入Facade模式内
外观模式的目的在于降低系统的复杂程度,简单来看是接口(Api)变少了,使用更简单了,并且降低了类与类之间的耦合度
案例
模拟一套较完整的用户注册登录认证流程,尽可能让用户无感知,要求:
- 用户输入账号、密码和身份(普通和超管)注册
- 用户输入账号密码登录
- 注册要保证账号不重复
- 判断账号密码正确性,正确则成功登录,并将用户的账号和权限签名为一个token返回去
- 已登录时,重复登录,可以刷新token的时间,但不提醒用户
- 有2个资源(base, plus)可供访问,后者只有超管能访问
用户子系统
User
public class User {
private String name;
private String pwsd;
private String role;
private String token;
// 省略了构造,get/set,和euqal
// equal的判断依据是name和pwsd都相等
}
UserList
import java.util.ArrayList;
public class UserList {
private ArrayList<User> users;
public UserList() {
users = new ArrayList<>();
}
public User find(User user){
int index = users.indexOf(user);
return index!=-1?users.get(users.indexOf(user)):null;
}
public void add(User user){
users.add(user);
}
}
Token
import java.util.HashMap;
import java.util.Map;
public class Token {
public String value;
private Token(User user) {
Map<String,String> payloads = new HashMap<>();
payloads.put("name", user.getName());
payloads.put("role", user.getRole());
value = payloads.toString();
}
public static Token sign(User user) {
return new Token(user);
}
public static Map<String, String> getPayloads(String v) {
v = v.substring(1, v.length()-1);
String[] subs = v.split(",");
Map<String,String> payloads = new HashMap<>();
for (String substring : subs) {
String key = substring.split("=")[0];
String value = substring.split("=")[1];
String key1 = key.trim();
String value1 = value.trim();
payloads.put(key1, value1);
}
return payloads;
}
public Map<String, String> getPayloads() {
String v = value;
return getPayloads(v);
}
}
Auth
public class Auth {
public static boolean checkLogin(User user) {
return user.getToken()!=null;
}
public static boolean checkRole(String resource, User user) {
if (resource.equals("plus")){
return Token.getPayloads(user.getToken()).get("role").equals("超管");
}
return true;
}
}
用户子系统仅仅为了简单模拟一下用户注册登录认证,就创建了这么多类和方法,如果直接丢给客户端用,既让人懵逼也不安全,因此我们要封装一个高级接口,利用用户系统,提供3个功能即可,即login(登录),register(注册)和logout(登出)
UserFacade
import UserSystem.Auth;
import UserSystem.Token;
import UserSystem.User;
import UserSystem.UserList;
public class UserFacade {
private UserList userList = new UserList();
void register(String name, String pwsd, String role) {
User user = new User(name, pwsd, role);
User user1 = userList.find(user);
if (user1!=null){
System.out.println("已注册过");
}
userList.add(user);
System.out.println("注册成功");
}
User login(String name, String pwsd) {
User user = new User(name, pwsd);
User user1 = userList.find(user);
if (user1!=null){
if (user.getToken()==null){
System.out.println("登录成功");
}
Token token = Token.sign(user);
user.setToken(token.value);
} else {
System.out.println("登录失败");
}
return user;
}
User logout(User user){
user.setToken(null);
return user;
}
boolean visit(String resource, User user) {
if (!Auth.checkLogin(user)) {
System.out.println("请先登录");
return false;
};
if (!Auth.checkRole(resource, user)) {
System.out.println("没有权限");
return false;
};
return true;
}
}
UserFacade封装好了注册、登入和登出的流程,并且还打印了反馈信息,只需寥寥几个参数即可使用,接着我们在Main(客户端)中使用UserFacade注册一个普通账号并登入它去访问指定资源
Client
import UserSystem.User;
public class Main {
public static void main(String[] args) {
UserFacade userFacade = new UserFacade();
//注册
userFacade.register("evan", "123", "普通");
//登录1
User user = userFacade.login("evan", "root");
//登录2
user = userFacade.login("evan", "123");
//访问plus
userFacade.visit("plus", user);
//退出登录
user = userFacade.logout(user);
//访问base
userFacade.visit("base", user);
}
}
Output:
D:\Develop-Environment\Java\jdk-1.8\bin\java.exe...
注册成功
登录失败
登录成功
登录失败
没有权限
请先登录
用了UserFacade,客户马上就能明白怎么捣鼓他的账号,这就是外观模式的妙处。据统计,外观模式可是我们在项目开发中最广泛应用到的一种设计模式,很多时候子系统代码层次较多,为了方便开发者专注于较上层,在中间也会加一层外观者。