第十七周_S-设计模式-结构型-装饰器模式

52 阅读3分钟

IO 流了解吗?里面的设计模式是什么?由一道面试题引发的本文。「装饰器模式」

定义

装饰器模式:在不改变原有类的基础上给类增加新的功能。

「AOP、继承也都能做到,但是装饰器可以避免过多的子类继承,也能避免aop的复杂性」

主要特点“

  • 不改变原类文件
  • 不适用继承
  • 动态扩展

代码实现

这里模拟单点登录的情况:

  1. 开始只需要验证用户是否登录成功
  2. 后续可能会根据每个账号角色设定不同的权限

下面是公共类:

/**
 * @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 ? " 放⾏行行" : " 拦截"));
    }

总结

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类增强功能。这也是判断是否该用装饰器模式的一个重要依据。除此之外,还可以对原始类嵌套使用多个装饰器。为了满足这个场景,装饰器类需要跟原始类继承相同的抽象类或者接口。

如果仔细看上面的代码,你会发现类结构图和开头的图片的类结构图是一模一样的。