设计模式——职责链模式

前言

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

讲个小故事——“部门请假”

今天我们来讨论下职责链模式,但在详细讲解前,我们先来看个场景。

《设计模式——职责链模式》

在工作中,请假是在所难免,有时是比较短时间的请假,例如:请一天假,陪陪女朋友逛街,这个时候只需要请示下部门组长;请三天假,去一个短暂的徒步旅程,这个时候需要请示下部门经理;请五天假,回家陪陪父母,这个时候需要请示下CTO。
在没有任何模式指导的情况下,我们会这么来写这个场景:

public class NoDesign {

    public static void main(String[] args) {
        int level = 1;

        if (level <= 1) {
            System.out.println("我是组长,批准!");
        } else if (level <= 3) {
            System.out.println("我是部门经理,批准!");
        } else if (level <= 5) {
            System.out.println("我是CTO,批准!");
        } else {
            System.out.println("没人处理,不批准!");
        }
    }

}

大家会发现,这是一段有臭味的代码,一旦业务需求有变,修改人员(有可能是新接手的程序猿)需要去阅读整串代码,然后进行修改。如果逻辑非常复杂,有可能会有牵一发动全身的问题。换个装bility的说法,也就是职责不分明,引起这个类的变化的原因不止一个;而且也违背了开闭原则,扩展的时候需要改到之前的类。总之,需要重构。

如何进行重构

这种由请求者发起请求,处理者们根据不同的条件进行处理的场景(例如:普通客户—VIP—SVIP)。其实可以考虑用职责链模式来进行处理。
以下便是职责链的通用类图,现在我们就把刚才的场景一一对照,提出请假的人便是Client,而组长、经理、CTO三者其实是同一类人,即都是请求处理者(ConcreteHandler),而他们都是抽象的处理者(Handler)的子类。

《设计模式——职责链模式》 职责链模式

按照通用类图进行重构后,类图如下,分为五个类:

  1. Client: 调用者;
  2. Request: 请求体,用于封装一些请求的信息,有时候判断的因素不只是一个;
  3. Response: 结果实体,消息处理完封装的结果类;
  4. Handler: 处理请求的实体类;
  5. Level: 等级类,用于保存当前等级的一些消息,如果逻辑简单可以和Request类合并;

    《设计模式——职责链模式》

编码时刻

说了那么多,主要还是编码,我们先来看Handler:

public abstract class Handler {

    //指向下一个处理类
    private Handler nextHandler;

    public final Response handleMessage(Request request) {
        Response response = null;

        //判断是否是自己的处理等级
        if (request.getLevel().getDay() <= this.getHandlerLevel().getDay()) {
            //是自己的处理等级,就将其进行处理
            response = this.echo(request);
        } else {

            //如果还有下一个handler
            if (this.nextHandler != null) {
                //如果有就让下一个handler进行处理
                response = this.nextHandler.handleMessage(request);
            } else {
                //没有适当的处理者,由调用者自己处理,获取不处理
            }

        }

        return response;

    }

    /**
     * @date 创建时间 2018/3/28
     * @author Jiang zinc
     * @Description 设置下一个handler处理类
     * @version
     */
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    /**
     * @date 创建时间 2018/3/28
     * @author Jiang zinc
     * @Description 模版方法,由子类决定当前实现的实体类是什么等级
     * @version
     */
    protected abstract Level getHandlerLevel();

    /**
     * @date 创建时间 2018/3/28
     * @author Jiang zinc
     * @Description 对任务的具体处理操作
     * @version
     */
    protected abstract Response echo(Request request);
}
public class CTOHandler extends Handler {
    @Override
    public Level getHandlerLevel() {
        return new Level("技术总监", 5);
    }
    @Override
    public Response echo(Request request) {
        return new Response("我是"+getHandlerLevel().getLevelName()+",我准了!");
    }
}
public class ManagerHandler extends Handler {
    @Override
    public Level getHandlerLevel() {
        return new Level("经理",3);
    }
    @Override
    public Response echo(Request request) {
        return new Response("我是"+getHandlerLevel().getLevelName()+",我准了!");
    }
}
public class TeamLeaderHandler extends Handler {
    @Override
    public Level getHandlerLevel() {
        return new Level("组长", 1);
    }
    @Override
    public Response echo(Request request) {
        return new Response("我是"+getHandlerLevel().getLevelName()+",我准了!");
    }
}

在Handler中,由handleMessage方法进行判断是否交由当前的handler进行处理,在这个抽象类中使用了模版方法(getHandlerLevel和echo),将Handler的等级和处理逻辑延迟到子类进行处理。所以子类的handler只需要处理好当前自己等级的逻辑即可,然后基类的handler做好链的调用逻辑处理(即什么情况交由当前handler,什么情况传递下一个处理),最后等待拼装成链给到用户(即例子中的请假人)使用。
接下来看下请求类、结果类、等级类:

public class Request {
    private Level mLevel;
    public Request(Level mLevel) {
        this.mLevel = mLevel;
    }
    public Level getLevel() {
        return this.mLevel;
    }
}
public class Response {
    private String content;
    public Response(String content) {
        this.content = content;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}
public class Level {
    private String levelName;
    private int day;
    public Level(String levelName, int day) {
        this.levelName = levelName;
        this.day = day;
    }
    public Level(int day) {
        this.day = day;
    }
    public String getLevelName() {
        return levelName;
    }
    public void setLevelName(String levelName) {
        this.levelName = levelName;
    }
    public int getDay() {
        return day;
    }
    public void setDay(int day) {
        this.day = day;
    }
}

这几个类比较简单,只是常规的bean,在真正的使用中,可能会减少也有可能会增多,这个视情况而定。
最后就是使用职责链了,这里使用了一个ResponChainHelper辅助类,进行职责链的拼装,让client无需知道拼装过程。从client类中,可看到,这时只需要获取到职责链的handler,实例化一个request,交由handler处理即可,至于谁处理,对于用于来说是透明的。

public class ResponChainHelper {
    public static Handler getResChain() {
        Handler ctoHandler = new CTOHandler();
        Handler managerHandler = new ManagerHandler();
        Handler teamLeaderHandler = new TeamLeaderHandler();

        teamLeaderHandler.setNextHandler(managerHandler);
        managerHandler.setNextHandler(ctoHandler);
        return teamLeaderHandler;
    }
}
public class Client {

    public static void main(String[] args) {
        Handler handler = ResponChainHelper.getResChain();

        System.out.println("我想要请一天假:");
        Request request1 = new Request(new Level(1));
        Response response1 = handler.handleMessage(request1);
        System.out.println(response1.getContent());

        System.out.println("我想要请三天假:");
        Request request2 = new Request(new Level(3));
        Response response2 = handler.handleMessage(request2);
        System.out.println(response2.getContent());

        System.out.println("我想要请五天假:");
        Request request3 = new Request(new Level(5));
        Response response3 = handler.handleMessage(request3);
        System.out.println(response3.getContent());
    }

}

《设计模式——职责链模式》 运行结果

如何扩展

如果此时,需求变动,需要增加一项:请假10天的需要CEO批准(这里只是打个比方,现实的CEO很忙不会有空管理这些事),只需要继承handler,然后编写自己需要处理的逻辑,最后在helper中加入到指责链,其余的所有代码都无需改动。

public class CEOHandler extends Handler {
    @Override
    protected Level getHandlerLevel() {
        return new Level("CEO", 10);
    }
    @Override
    protected Response echo(Request request) {
        return new Response("我是" + getHandlerLevel().getLevelName() + ",我准了!");
    }
}
public class ResponChainHelper {
    public static Handler getResChain() {
        Handler ctoHandler = new CTOHandler();
        Handler managerHandler = new ManagerHandler();
        Handler teamLeaderHandler = new TeamLeaderHandler();
        Handler ceoHandler = new CEOHandler();

        teamLeaderHandler.setNextHandler(managerHandler);
        managerHandler.setNextHandler(ctoHandler);
        ctoHandler.setNextHandler(ceoHandler);
        return teamLeaderHandler;
    }
}

职责链模式的缺点

  1. 性能问题:每次请求都会从职责链的链头开始处理,当职责链比较长的时候,性能就会成为一个问题。
  2. 调试不便:假设某个环节除了什么问题,当职责链比较长时,需要确定是哪个handler出现问题是比较麻烦的,因为职责链有些类似递归处理,过长的链,层级的增加会导致逻辑复杂。

如何避免这些缺点?主要通过职责链的长度,以阀值来限制职责链的长度。

写在最后

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

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

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