设计模式-结构型模式(装饰器模式)

1、装饰器模式

含义:

          创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

特征:

           1、可以透明动态的扩展一个类的功能或给一个类添加附加职责;   

           2、动态的给一个对象添加功能,这些功能可以再动态的撤销;

使用场景:

           扩展一个类功能;   动态增加功能,动态撤销; 

           当存在较多复杂的类功能组合的时候,为避免类爆炸,就会采用装饰器模式;

命名规则:

           Decorator

现实案例:

          咖啡: +冰  + 糖 + 奶 

          煎饼: +生菜 +鸡蛋 + 火腿肠 + 肉松

          蛋糕: +水果 + 巧克力 + 奶油

 

2、装饰器模式通用实现:

装饰器模式的 UML 图

Shape接口定义,提供 draw() 的画画公用方法

ShapeDecorator,实现抽象的方法,包含一个Shape属性,通过构造方法传入,实现draw()方法;

RedShapeDecorator   实现ShapeDecorator,添加自身专属方法setRedBorder()  红边设置;实现增强类实现;

Circle 和 Rectangle  实现接口Shape, 实现普通的draw方法;

 

 

 

3、在源码中的使用

案例1:InputStream

inputstream   

①InputStream接口定义,提供 draw() 的画画公用方法;

②FilterInputStream,实现抽象装饰角色,包含一个InputStream属性,通过构造方法传入,实现所有规定接口;

③具体装饰角色:BufferedInputStream、DataInputStream、HttpInputStream

 

案例2:CachingExecutor(Mybatis)

  

https://blog.csdn.net/qq_18860653/article/details/80594778

 

案例3:TransactionAwareCacheDecorator(Spring)

Cache 接口定义
AbstractValueAdaptingCache 缓存的抽象实现
GuavaCache  包装的guava缓存
TransactionAwareCacheDecorator   事务包装缓存

 

 

 

 

4、真实项目案例(装饰器对请求数据进行复杂的加密、解密)

项目背景  :

      在金融项目数据加密是非常常见的,比如前端将加密后的字符串作为参数传给我们, 我们在接口中解密,再比如我们后端返回给前端的结果也是一串加密后的字符串,这是防止爬虫的一种安全手段,那么我们是否需要在每个接口在去进行加密解密?是否有一套机制去控制我想要加密的接口,同学们应该会想到只需在一个请求到达Controller之前能够截获其请求,并且根据其具体情况对 HttpServletRequest 中的参数进行过滤或者修改。然后再放回到该HttpServletRequest 中呢?

      流只能读取一次,读取前端传过来的参数具体参数,如果是post请求,那么getInputStream()可以解析到详细的参数

      在正式代码之前,我还是先简单介绍下ServletRequest、HttpServletRequest、ServletRequestWrapper以及HttpServletRequestWrapper这几个接口或者类之间的层次关系,并说明“继承HttpServletRequestWrapper类以实现在Filter中修改HttpServletRequest的参数”这种方式的原理是什么

它们之间的层次关系是这样的:

如上图所示,在过滤器中:

//我们会进入这个方法
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

//通过ServletRequest进来的,之后我们会强制造型如下:
 HttpServletRequest req=(HttpServletRequest) request;
''''''
//最后调用
chain.doFilter(req, res);

如果需要对前端加密过来的参数进行解密,那么我们需要对HttpServletRequest进行处理,而上面图关系毫无疑问是一个装饰模式:

以在HttpServletRequestWrapper 做具体的装饰器

public class ParamsWrapperDecorator extends HttpServletRequestWrapper {

    private static final Logger logger = LoggerFactory.getLogger(ParamsWrapperDecorator.class);

    private HttpServletRequest request;

    public ParamsWrapperDecorator(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException, RoborderException {
        String path = request.getRequestURI();
        //if (new Random().ints(0, 4).boxed().findFirst().orElse(-1) == 1) {
            LoggerUtil.showInfoLogDetails(logger, "IP:{}, 请求地址:{}",Optional.ofNullable(MDC.get("ip")).orElse("unknown"),path);
       // }
        final MethodRule  rule = MethodRule.getEnum(path);
        //处理传入传入参数是否加密
        ServletInputStream is = handleWithParamsDecrypt(rule);
        return is == null ? request.getInputStream() : is;

    }

    private ServletInputStream handleWithParamsDecrypt(final MethodRule rule) throws IOException, RoborderException {
        if (Optional.ofNullable(rule).isPresent() && rule.getParamsDecrypt()) {
           // 大致业务逻辑代码
            BufferedReader reader = request.getReader();
            String encryptData = String.join("", reader.lines().collect(Collectors.toList()));
            reader.close();
           .......................................
            String result;
            EncapDataDTO dto = JSONObject.parseObject(encryptData, EncapDataDTO.class);
            try {
                result = Optional.of(Des3Util.decode(dto.getEncryptData(), secretKey)).orElse("");
                LoggerUtil.showInfoLogDetails(logger, "真实传入参数:{}, 传入参数encaptData:{}, 密钥secretKey:{}", result, encryptData, secretKey);
            } catch (Exception e) {
                LoggerUtil.showErrorDetails(logger, "参数解密失败,加密的数据为:{}", dto.getEncryptData());
                throw new RoborderException(MemberErrorCode.DATA_TRANSFORM_ERROR);
            }
            ByteArrayInputStream is = new ByteArrayInputStream(result.getBytes(Charset.forName("utf8")));

            return new ServletInputStream() {

                @Override
                public boolean isFinished() {
                    return false;
                }
                @Override
                public boolean isReady() {
                    return false;
                }
                @Override
                public void setReadListener(ReadListener listener) {
                }
                @Override
                public int read() throws IOException {
                    return is.read();
                }
            };
        }
        return null;
    }

}

定义接口规则枚举类

public enum MethodRule {

    start("start", true, true, EncryptType.BASE64, 0),
    Member_registerUser(RequestUrl.Member + RequestUrl.Member_registerUser, false, false, EncryptType.NULL, 0),
    end("end", true, true, EncryptType.RSA, 0);

    private final String url;

    /**
     * 参数是否需要解密
     */
    private final Boolean paramsDecrypt;

    /**
     * 返回结果是否需要加密
     */
    private final Boolean resultEncrypt;

    /**
     * 加密方式
     */
    private final EncryptType signType;

    /**
     * 返回数据是否需要压缩 0 不压缩, 1 压缩
     */
    private final Integer dataCompress;


    MethodRule(String url, Boolean paramsDecrypt, Boolean resultEncrypt, EncryptType signType, Integer dataCompress) {
        this.url = url;
        this.paramsDecrypt = paramsDecrypt;
        this.resultEncrypt = resultEncrypt;
        this.signType = signType;
        this.dataCompress = dataCompress;
    }

    public static MethodRule getEnum(final String url) {
        return Arrays.stream(MethodRule.values()).filter(method -> url.startsWith(method.URL()))
                .findFirst().orElse(null);
    }

    public String URL() {
        return url;
    }

    public Boolean getParamsDecrypt() {
        return paramsDecrypt;
    }

    public Boolean getResultEncrypt() {
        return resultEncrypt;
    }

    public EncryptType getSignType() {
        return signType;
    }

    public Integer getDataCompress() {
        return dataCompress;
    }

}

controller层

    @ApiOperation(value = "用户注册", notes = "userDTO 可选参数:推荐人userToken  推荐码referralCode,其他为必选参数 ", response = ResultVO.class)
    @RequestMapping(value = RequestUrl.Member_registerUser, method = RequestMethod.POST)
    public ResultObject<ResultVO> registerUser(){
}

最后是dofliter中调用

 HttpServletRequestWrapper params = new ParamsWrapperDecorator(req);
 ResultWrapperDecorator result = new ResultWrapperDecorator(res);
 chain.doFilter(params, result);

 

https://nicky-chen.github.io/2018/03/28/decorator/

 

5、优缺点评估:

       优点:

          1、是继承的有效替代模式,可以动态的扩展一个实现类的功能,比继承灵活;

          2、装饰类和被装饰类可以独立发展,不会相互耦合

      缺点: 

          增加系统的复杂性,会实现更多的类和代码;    使得代码复杂

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值