设计模式——装饰模式

前言

设计模式是指导一个程序猿以更好的姿态处理一些问题,而不再像刚学编程的我们,只会使用if-else分支语句,或是使用硬干的骚操作完成需求。不使用设计模式,一来是代码逻辑会越来越晦涩难懂(到了某天你会发现自己也没办法看清楚所有逻辑),二来是代码维护成本越来越高(你的加班时间会越来越长),三来是可以装bility。基于这么多的好处,小盆友和大家一起讨论和分享下设计模式,让自己不再是一个坐在电脑前敲代码的码农。

讲个小故事——“加密请求”

老套路,讲装饰模式前,先讲个故事作为场景

《设计模式——装饰模式》

之前的一家公司是做保险类业务的,需要跟保险公司进行对接获取保险的一些信息。但是,请求的内容需要进行加密,而且加密的过程比较繁琐,这里我们为了方便讲解,就虚构一个加密流程。保险公司规定,需要对内容进行md5加密,然后再进行sha加密。
我们很理所当然的设计,是这样子的:

public class NoDesign {

    public static void main(String[] args){

        String content = "我是猛猛的小盆友。";
        
        content = "已经Md5加密:【" + content + "】";
        
        content = "已经sha加密:【" + content + "】";
        
        System.out.println(content);

    }
    
}

看起来,很简洁没毛病。但是,IT行业唯一不变的就是变动的需求。某天,保险公司说A接口,只要md5加密就可以;B接口是要先sha加密,后再md5;C接口保持不变,还是先md5再sha加密;D接口只要个sha加密就可以(这里把变动极端化了,但是现实确实也有类似的场景,我们就不钻牛角尖了哈)。这个时候,这简洁的代码便不再简洁了,而且这里只是简单的打印,真实的场景会要求将内容按一定顺序拼接在进行md5之类的要求,此时这段加密代码便会面目全非。所以,需要进行重构。

如何进行重构

在这个场景中,主要是围绕着请求内容来,而无论是md5还是sha加密,或是以后增加其他加密方式,都是作为请求体的一个增强功能,如果这里使用继承这种骚操作,可想而知,子类会膨胀,因为上面的场景就需要4个子类来对应。
加密的方式其实并不是请求体的必备,而只是一个附带的功能。这个时候可以考虑用装饰模式进行重构。
我们先看看装饰模式的通用类图。在场景中的核心是请求内容,对应着类图的ConcreateComponent,也就是我们要进行装饰的类;而各种加密的类对应着ConcreteDecorator,就是带有装饰功能的类,他们都继承于一个装饰的抽象类(Decorator);而所有的这些类最终又归于Component类,这样使得最后的结果类型是一致的,至于过程怎么包装都不会影响到接收的类型。

《设计模式——装饰模式》 装饰模式通用类图

经过上面的分析后,我们的类图变为如下:

《设计模式——装饰模式》 image.png

编码时刻

说了那么多,主要还是编码,我们先来看一切的开始Request:

public abstract class Request {
    public abstract String getReqResult();
}

主要是提供一个方法,让其装饰类通过这个方法获取上个装饰类或被装饰类需要被装饰的内容(这里有点绕,多看两遍,其实就是通过这个方法获取需要被装饰的内容)。

接下来看被装饰类OrginalRequest:

public class OriginalRequest extends Request {
    private String content;
    public OriginalRequest(String content) {
        this.content = content;
    }
    @Override
    public String getReqResult() {
        return content;
    }
}

这里设置了真正的请求内容,然后通过方法对外提供,至于后续这个内容被怎么加工,其实对其来说已经是透明的。

再来看看装饰类RequestDecorator

public abstract class RequestDecorator extends Request {

    private Request mRequest;

    /**
     *
     * @date 创建时间 2018/3/28
     * @author Jiang zinc
     * @Description 设置需要装饰的被装饰类
     * @version
     *
     */
    public RequestDecorator(Request request) {
        this.mRequest = request;
    }

    @Override
    public String getReqResult() {
        return mRequest.getReqResult();
    }

}

这里是一个简单的代理,直接调用设置进来的Request。主要是看其实现的子类Md5Request和SHARequest,这两个类分别先获取需要装饰的内容,然后进行各自内部的逻辑运行,装饰模式的优势自然而然的就体现出来了,算法间的逻辑是没有耦合的,当需要变动的时候,找到负责的类进行修改便可,不会担心修改到其他逻辑。

public class Md5Request extends RequestDecorator {

    public Md5Request(Request request) {
        super(request);
    }

    @Override
    public String getReqResult() {

        String reqResult = super.getReqResult();
        System.out.println("Md5加密");

        return "已经Md5加密:【"+reqResult+"】";
    }
}
public class SHARequest extends RequestDecorator {

    public SHARequest(Request request) {
        super(request);
    }

    @Override
    public String getReqResult() {

        String reqResult = super.getReqResult();
        System.out.println("sha加密");

        return "已经sha加密:【" + reqResult+"】";
    }

}

最后来瞧瞧如何使用。从使用便知道了,像上面所说的需求变动,这里的修改是非常简便的,而且逻辑很清晰。

public class Client {
    public static void main(String[] args){
        Request request = new OriginalRequest("我是猛猛的小盆友。");
        request = new Md5Request(request);
        request = new SHARequest(request);

        System.out.println(request.getReqResult());
    }
}

《设计模式——装饰模式》 运行结果

如何扩展

如果此时保险公司说,他们自己研发的lala加码算法,请求内容需要先sha加密,然后lala加密,最后md5加密。那我们就可这么扩展了,编写一个LalaRequest继承RequestDecorator,代码如下:

public class LalaRequest extends RequestDecorator {

    public LalaRequest(Request request) {
        super(request);
    }

    public String doSomething(){
        return "lala=====";
    }

    @Override
    public String getReqResult() {
        String reqResult = super.getReqResult();
        System.out.println("lala加密");
        return this.doSomething()+reqResult;
    }
}

然后在调用的时候,组装下就可以使用了:

public class Client {

    public static void main(String[] args) {
        Request request1 = new OriginalRequest("我是猛猛的小盆友。");
        request1 = new Md5Request(request1);
        request1 = new SHARequest(request1);

        System.out.println(request1.getReqResult());

        Request request2 = new OriginalRequest("我是猛猛的小盆友。");
        request2 = new SHARequest(request2);
        request2 = new LalaRequest(request2);
        request2 = new Md5Request(request2);

        System.out.println(request2.getReqResult());

    }

}

《设计模式——装饰模式》 image.png

装饰模式的缺点

当装饰的层次过多时,复杂度也会随着上升,所以在使用该模式时,要限制装饰的层次过多问题。

写在最后

本篇文章没有长篇大论的讲理论,因为这不是我的风格,更多的是通过前后的代码对比和一些编码时微妙的变化入手,如果这篇文章对你有所启发请给个“❤️”鼓励下吧。

代码地址:https://github.com/zincPower/JDesign

    原文作者:猛猛的小盆友
    原文地址: https://www.jianshu.com/p/d0643c4f12e6
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞