IO 流了解吗?里面的设计模式是什么?由一道面试题引发的本文。「装饰器模式」
定义
装饰器模式:在不改变原有类的基础上给类增加新的功能。
「AOP、继承也都能做到,但是装饰器可以避免过多的子类继承,也能避免aop的复杂性」
主要特点“
- 不改变原类文件
- 不适用继承
- 动态扩展
代码实现
这里模拟单点登录的情况:
- 开始只需要验证用户是否登录成功
- 后续可能会根据每个账号角色设定不同的权限
下面是公共类:
/**
* @description:
* 模拟 Spring 的 HandlerInterceptor
*
* 实际开发中会使用 Spring 的 HandlerInterceptor 类
* @author:
* @date:
*/
public interface HandlerInterceptor {
public boolean preHandle(String request, String response, Object handler);
}
public class SsoInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(String request, String response, Object handler) {
// 模拟获取cookie
String ticket = request.substring(1, 8);
// 模拟校验
return ticket.equals("success");
}
}
初始代码
直接实现 HandlerInterceptor 做逻辑判断:只判断是否登录成功
/**
* @description:
* 通过继承将个⼈人可访问哪些⽅方法的功能添加到⽅方法中。但是场景很多,就很麻烦了
* @author:
* @date:
*/
public class LoginSsoDecorator extends SsoInterceptor {
private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();
static {
authMap.put("huahua", "queryUserInfo");
authMap.put("doudou", "queryUserInfo");
}
@Override
public boolean preHandle(String request, String response, Object
handler) {
// 模拟获取cookie
String ticket = request.substring(0, 7);
// 模拟校验
boolean success = ticket.equals("success");
if (!success) {return false;}
String userId = request.substring(7);
String method = authMap.get(userId);
// 模拟⽅方法校验
return "queryUserInfo".equals(method);
}
}
装饰器模式
-
是一个抽象类,只是实现了 HandlerInterceptor
-
当装饰角色继承接口后会提供构造函数,入参就是继承的接口实现类即可,这样就能很方便的扩展出不同功能组件
-
在装饰类中有两个重点的地方是:继承了处理接口;提供了构造函数;覆盖了方法 preHandle
public abstract class SsoDetector implements HandlerInterceptor {
private HandlerInterceptor handlerInterceptor;
private SsoDetector(){}
public SsoDetector(HandlerInterceptor handlerInterceptor) {
this.handlerInterceptor = handlerInterceptor;
}
public boolean preHandle(String request, String response, Object
handler) {
return handlerInterceptor.preHandle(request, response, handler);
}
}
public class LoginSsoDecorator extends SsoDetector{
private Logger logger =
LoggerFactory.getLogger(LoginSsoDecorator.class);
private static Map<String, String> authMap = new
ConcurrentHashMap<String, String>();
static {
authMap.put("huahua", "queryUserInfo");
authMap.put("doudou", "queryUserInfo");
}
public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {
super(handlerInterceptor);
}
@Override
public boolean preHandle(String request, String response, Object
handler) {
boolean success = super.preHandle(request, response, handler);
if (!success) {return false;}
String userId = request.substring(8);
String method = authMap.get(userId);
logger.info("模拟单点登录⽅方法访问拦截校验: {} {}", userId, method);
// 模拟⽅方法校验
return "queryUserInfo".equals(method);
}
}
测试
//使用继承扩展功能的测试方法
@Test
public void test_LoginSso() {
LoginSsoDecorator ssoDecorator = new LoginSsoDecorator();
String request = "successhuahua";
boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
System.out.println("登录校验: " + request + (success ? " 放⾏行行" : " 拦截"));
}
//使用装饰器模式扩展的功能的测试方法
// 将初始功能包装起来 new SsoInterceptor(),这样新的类就同时有了初始功能和新类功能
@Test
public void test_LoginSsoDecorator() {
com.practic.thinkinpatterndesign.结构型模式.装饰器模式.模式实现.LoginSsoDecorator ssoDecorator =
new com.practic.thinkinpatterndesign.结构型模式.装饰器模式.模式实现.LoginSsoDecorator(new SsoInterceptor());
String request = "1successhuahua";
boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
System.out.println("登录校验: " + request + (success ? " 放⾏行行" : " 拦截"));
}
总结
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类增强功能。这也是判断是否该用装饰器模式的一个重要依据。除此之外,还可以对原始类嵌套使用多个装饰器。为了满足这个场景,装饰器类需要跟原始类继承相同的抽象类或者接口。
如果仔细看上面的代码,你会发现类结构图和开头的图片的类结构图是一模一样的。