type
status
date
slug
summary
tags
category
icon
password

一、定义

notion image
装饰器模式:初看上图感觉装饰器模式有点像俄罗斯套娃、某众汽车,而装饰器的核心就是在不改变原有类的基础上给类新增功能。不改变原有类,可能有的小伙伴会想到继承、AOP切面。可以避免继承导致的子类过多

二、示例:

模拟场景:
例如,公司内部的sso单点登录已经提供了实现类,并有一套固定的校验体系。现在,因为公司业务扩张需要新增校验规则,同时还要保证原有的功能不被破坏。这时,我们可以考虑使用装饰器模式,扩充原有的单点登录功能。
notion image

基础设计

要想实现拦截的功能,就必须实现此接口HandlerInterceptor
package com.qf.design.structure.decorate.basic; public interface HandlerInterceptor {    /**     * prehandler方法拦截,需要被实现     */    boolean preHandler(String request,String response,Object handler); }
父类的基础拦截功能:SsoInterceptor
package com.qf.design.structure.decorate.basic; public class SsoInterceptor implements HandlerInterceptor{    /**     * 父类的基础拦截功能     * @param request     * @param response     * @param handler     * @return     */    @Override    public boolean preHandler(String request, String response, Object handler) {        //模拟获取cookie        String substring = request.substring(1, 8);        //是否拦截        return substring.equals("success");   } }

传统的编码方式

重复书写父类实习代码,一旦父类的类变化了,又需要新写子类继承
package com.qf.design.structure.decorate.tradition; import com.qf.design.structure.decorate.basic.SsoInterceptor; import java.util.HashMap; import java.util.Map; public class LoginSsoDecoder extends SsoInterceptor {    public LoginSsoDecoder(){}    private static Map<String,String> authMap=new HashMap<>();    static {        authMap.put("huahua", "queryUserInfo");        authMap.put("doudou", "queryUserInfo");   }    @Override    public boolean preHandler(String request, String response, Object handler) {        //模拟获取cookie        String substring = request.substring(1, 8);        //是否拦截        boolean success = substring.equals("success");        if (!success) return false;        //自己需要拓展的拦截逻辑        String userId = request.substring(9);        String method = authMap.get(userId);        // 模拟方法校验        return "queryUserInfo".equals(method);   } }
测试:ApiTest
package com.qf.design.structure.decorate.tradition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ApiTest {    private static Logger logger= LoggerFactory.getLogger(ApiTest.class);    public static void main(String[] args) {        LoginSsoDecoder loginSsoDecoder=new LoginSsoDecoder();        String request="ssuccessshuahuas";        boolean result = loginSsoDecoder.preHandler(request, "response", "");        logger.info("登录校验:"+request+(result?"通过":"失败"));   } }

装饰器模式设计

作为中转站,接受传递的子类,并调用子类的preHandler 被继承的接⼝可以通过构造函数传递其实现类
package com.qf.design.structure.decorate.design; import com.qf.design.structure.decorate.basic.HandlerInterceptor; public abstract class SsoDecoder implements HandlerInterceptor {    private HandlerInterceptor handlerInterceptor;    public SsoDecoder(HandlerInterceptor handlerInterceptor){        this.handlerInterceptor=handlerInterceptor;   }    @Override    public boolean preHandler(String request, String response, Object handler) {        return handlerInterceptor.preHandler(request,response,handler);   } }
package com.qf.design.structure.decorate.design; import com.qf.design.structure.decorate.basic.HandlerInterceptor; import java.util.HashMap; import java.util.Map; public class LoginSsoDecoder extends SsoDecoder{    private static Map<String,String> authMap=new HashMap<>();    static {        authMap.put("huahua", "queryUserInfo");        authMap.put("doudou", "queryUserInfo");   }    public LoginSsoDecoder(HandlerInterceptor handlerInterceptor){        super(handlerInterceptor);   }    @Override    public boolean preHandler(String request, String response, Object handler) {        //父类的拦截        boolean result = super.preHandler(request, response, handler);        if (!result) return false;        //自己需要拓展的拦截逻辑        String userId = request.substring(9);        String method = authMap.get(userId);        // 模拟方法校验        return "queryUserInfo".equals(method);   } }
测试:ApiTest
package com.qf.design.structure.decorate.design; import com.qf.design.structure.decorate.basic.SsoInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ApiTest {    private static Logger logger= LoggerFactory.getLogger(ApiTest.class);    public static void main(String[] args) {        LoginSsoDecoder loginSsoDecoder = new LoginSsoDecoder(new SsoInterceptor());        String request="ssuccessshuahua";        boolean result = loginSsoDecoder.preHandler(request, "response", "");        logger.info("登录校验:"+request+(result?"通过":"失败"));   } }

UML关系图

notion image
总结: 使⽤装饰器模式满⾜单⼀职责原则,你可以在⾃⼰的装饰类中完成功能逻辑的扩展,⽽不影响主类,同时可以按需在运⾏时添加和删除这部分逻辑。另外装饰器模式与继承⽗类重写⽅法,在某些时候需要需选择,并不⼀定某⼀个就是最好。
 
装饰器实现的重点是对抽象类继承接⼝⽅式的使⽤,同时设定被继承的接⼝可以通过构造函数传递其实现类,由此增加扩展性并重写⽅法⾥可以实现此部分⽗类实现的功能。假设基础主类发生了重大修改(改了类名或者新写),我们只需要传递最终所需要的主类
 
JAVA【设计模式】桥接模式JAVA【设计模式】外观模式