设计模式之责任链模式

场景:

中国古代对妇女制定了“三从四德” 的道德规范,“三从” 指 “未嫁从父、即嫁从夫、夫死从子”。也就是说,一位女性在结婚之前要听从于父亲,结婚后听从于丈夫,如果丈夫死了还要听从于儿子。比如说一位女性要去逛街,在她出嫁之前必须征得父亲的同意,出嫁之后必须获得丈夫的同意,如果丈夫死了, 那就要请示小叔子、侄子等。作为父亲,丈夫或儿子,只有两种选择:要不承担起来责任,允许她或不允许她逛街;要不就让他请示下一个人,这是整个社会体系的约束。应用到我们项目中就是业务规则。接下来我们就通过程序来实现“三从”制度。

设计类图如下

这里写图片描述

IHandler 是三个有决策权对象的接口,IWomen 是女性的接口代码,如下

/**
 * Created by yangyibo on 17/11/7.
 * 女人接口,未嫁从父,即嫁从夫,夫死从子。做什么事都要请示
 */
public interface IWomen {
    /**
     * 获得个人状况,是否结婚
     * @return
     */
    public int getType();

    /**
     * 获得个人请示,要干什么,逛街?约会? 看电影?
     * @return
     */
    public String getRequest();
}

女性接口就两个方法,一个是取得当前的个人状况getType ,通过返回值决定她当前的状态。另一个方法getRequest是要请示的内容,要逛街还是要吃饭。

古代妇女
/**
 * Created by yangyibo on 17/11/7.
 */
public class Women implements IWomen {
    private Integer type = 0;
    private String request = "";

    public Women(Integer type, String request) {
        this.type = type;
        this.request = request;
    }

    /**
     *  获取女人的个人情况
     *
     * @return 用数字代表女性的不同状态:0 未婚 1 出嫁 2 守寡
     */
    public int getType() {
        return this.type;
    }

    /**
     * 获取请求
     * @return
     */
    public String getRequest() {
        return this.request;
    }
}
有权处理者的接口
/**
 * Created by yangyibo on 17/11/7.
 */
public interface IHandler {
    /**
     * 一个女人要求逛街、等请求,需要处理
     * @param women
     */
    public void HandleMessage(IWomen women);
}
父亲类

未出嫁父亲有决定权

public class Father implements IHandler {

    //未出嫁的女儿的请示
    public void HandleMessage(IWomen women) {
        System.out.println("女儿的请示是:"+women.getRequest());
        System.out.println("父亲的回答: 同意");

    }
}
丈夫类

出家后丈夫有决定权

/**
 * Created by yangyibo on 17/11/7.
 * 丈夫类
 */
public class Husband implements IHandler {
    public void HandleMessage(IWomen women) {
        System.out.println("老婆的请示是:"+women.getRequest());
        System.out.println("丈夫的回答: 同意");
    }
}
儿子类

丈夫死了以后儿子有决定权

/**
 * Created by yangyibo on 17/11/7.
 */
public class Son implements IHandler {
    public void HandleMessage(IWomen women) {
        System.out.println("妈妈的请示是:"+women.getRequest());
        System.out.println("儿子的回答: 同意");
    }
}
场景类
/**
 * Created by yangyibo on 17/11/7.
 */
public class Client {
    public static void main(String[] args) {
        //随机生成几个女性
        Random rand = new Random();
        List<IWomen> womens = new ArrayList();
        for (int i = 0; i < 5; i++) {
            womens.add(new Women(rand.nextInt(3), "我要去逛街"));
        }
        IHandler father = new Father();
        IHandler husband = new Husband();
        IHandler son = new Son();
        for (IWomen women : womens) {
            if (women.getType() == 0) {
                // 未婚少女请示父亲
                System.out.println("\n--------女儿向父亲请示-------");
                father.HandleMessage(women);
            } else if (women.getType() == 1) {
                //已婚美女请示老公
                System.out.println("\n--------老婆向老公请示 -------");
                husband.HandleMessage(women);
            } else if (women.getType() == 2) {
                //妈妈向儿子
                System.out.println("\n--------妈妈向儿子请示-------");
                son.HandleMessage(women);
            } else {
                //暂时什么也不做
            }
        }
    }
}

通过随机方法产生5个古代妇女的对象,然后看她们是如何就逛街这件事去请示的,运行结果如下所示

--------女儿向父亲请示-------
女儿的请示是:我要去逛街
父亲的回答: 同意

--------妈妈向儿子请示-------
妈妈的请示是:我要去逛街
儿子的回答: 同意

--------女儿向父亲请示-------
女儿的请示是:我要去逛街
父亲的回答: 同意

--------女儿向父亲请示-------
女儿的请示是:我要去逛街
父亲的回答: 同意

--------老婆向老公请示 -------
老婆的请示是:我要去逛街
丈夫的回答: 同意

问题

“三从四德”的旧社会规范已经表现出来了,是不是觉得这个程序写的有点不舒服?有点别扭?那就对了! 这段代码有这几个问题:

  1. 指责界定不清晰
    对女儿提出的请示,应该在父类中作出决定,父亲有责任、有义务处理女儿的请求,因此Father类应该是知道女儿的请求自己处理,而不是在Client类中进行组装出来,也就是说原本应该是父亲这个类做的事情抛给了其他类进行处理,不应该是这样的。
  2. 代码臃肿
    我们在Client类中写了if…else的判断条件,而且能随着处理该类型的请示人员越多,if…else的判断就越多,臃肿的条件判断使可读性降低。
  3. 耦合过重
    我们要根据Women 的type 来决定使用IHandler 的那个实现类来处理请求。有一个问题是,如果IHandler的实现类继续扩展怎么办?修改CLient类?与开闭原则违背。
  4. 异常情况欠考虑
    如果妻子不懂什么三从四德 向自己的父亲请示了,父亲应该做如何处理?我们的程序没有体现出来。逻辑失败了!

好吧,既然这么多问题,我门就重新分析一下需求,女性提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说父亲做一个决断,而丈夫也做出了一个决断,也就是请示传递出去,必然有且只有一个唯一的处理人给出唯一答案。分析完毕,那我们重新设计。我们可以抽象成这样一个结构,女性的请求先发送到父亲类,父亲类一看是自己要处理的就作出回应处理,如果女儿已经出嫁了,就把这个请求转发到女婿来处理,如果女婿一旦去见上帝了,就由儿子来处理这个请求。

请求顺序如下:
这里写图片描述

父亲、丈夫、儿子每个节点由两个选择:要么承担责任,作出回应,要么把请求转发到后续环节。结构分析的已经很清楚了,那我们看怎么实现这个功能,类图如下:
这里写图片描述

三个实现类Father、Husband、Son 只要实现构造函数和父类中的抽象方法response就可以了,具体由谁处理女性提出的请求,都已经转移到了Hadnler抽象类中,我们来看Handler 怎么实现。

修改后的Handler
/**
 * Created by yangyibo on 2018/1/24.
 */
public abstract class Handler {

    public final static int FATHER_LEVEL_REQUEST = 0;
    public final static int HUSBAND_LEVEL_REQUEST = 1;
    public final static int SON_LEVEL_REQUEST = 2;
    public final static int GRANDSON_LEVEL_REQUEST = 3;

    //当前能处理的等级
    private int level;
    //下一个责任人
    private Handler nextHandler;

    public Handler(int level){
        this.level = level;
    }

    /**
     * 一个女人要求逛街、等请求,需要处理
     * 此处用到模版方法模式,在模版方法中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,作出反馈,如果不相等则传递到下一环节
     * @param women
     */
    public final void HandleMessage(IWomen women){
        if(women.getType() == this.level){
            response(women);
        }else{
            if(null != this.nextHandler) {
               // 有后续环节,将请求传递
                this.nextHandler.HandleMessage(women);
            }else{
                // 无后续环节。
                System.out.println("---没有后续环节,默认不同意---\n");
            }
        }
    }

    /**
     * 找下一个环节的责任人
     * @param _handler
     */
    public void setNext(Handler _handler){
        this.nextHandler = _handler;
    }

    //有请示就要有回应
    protected abstract void response(IWomen women);
}

其实这里也用到了模版方法模式,在模版方法模式中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,做出反馈;如果不相等,则传递到下一个环节,由下一个环节作出回应,如果已经达到了环节结尾则直接做不通同意处理。基本方法resoponse 需要各个实现类实现,每个实现类只要实现两个职责:一是定义自己能够处理的等级级别,二是对请求做出回应。

父亲类
/**
 * Created by yangyibo on 2018/1/25.
 */
public class Father extends Handler {

    public Father() {
        super(Handler.FATHER_LEVEL_REQUEST);
    }

    // 父亲的答复
    protected void response(IWomen women) {
        System.out.println("---女儿向父亲请示---");
        System.out.println(women.getRequest());
        System.out.println("父亲的回答: 同意");
    }
}
丈夫类
public class Husband extends Handler {

    public Husband() {
        super(Handler.HUSBAND_LEVEL_REQUEST);
    }

    //丈夫的答复
    protected void response(IWomen women) {
        System.out.println("---妻子向丈夫请示---");
        System.out.println(women.getRequest());
        System.out.println("丈夫的回答: 同意");
    }
}
儿子类
public class Son extends Handler{

    public Son() {
        super(Handler.SON_LEVEL_REQUEST);
    }
    //儿子的答复
    protected void response(IWomen women) {
        System.out.println("---母亲向儿子请示---");
        System.out.println(women.getRequest());
        System.out.println("儿子的答复是: 同意");
    }
}
新增了孙子类
public class Grandson extends Handler{

    public Grandson(){
        super(Handler.GRANDSON_LEVEL_REQUEST);
    }

    //孙子的答复
    protected void response(IWomen women) {
        System.out.println("---奶奶向孙子请示---");
        System.out.println(women.getRequest());
        System.out.println("孙子的回答: 同意");

    }
}

这几个类都很简单,构造方法是必须实现的,父类框定子类必须有一个显式构造函数,子类不实现,编译不通过。通过构造方法我们设置了各个类能处理的请求类型,如果没有相应的处理者,则视为不同意。

women类修改为:
public class Women implements IWomen {

    private int type = 0;
    private String request;

    public Women(int type, String request) {
        this.type = type;
        switch (type) {
            case 0:
                this.request = "女儿的请求是:" + request;
                break;
            case 1:
                this.request = "妻子的请求是:" + request;
                break;
            case 2:
                this.request = "母亲的请求是:" + request;
                break;
            case 3:
                this.request = "奶奶的请求是:" + request;
        }
    }

    public int getType() {
        return this.type;
    }

    public String getRequest() {
        return this.request;
    }
}
场景类
public class Client {
    public static void main(String[] args) {
        //生成请求
        Random rand = new Random();
        ArrayList<IWomen> arrayList = new ArrayList();
        for (int i = 0; i < 5; i++) {
            arrayList.add(new Women(rand.nextInt(4), "我要出去逛街"));

        }

        //构建责任链
        Handler father = new Father();
        Handler husband = new Husband();
        Handler son = new Son();
        Handler grandson = new Grandson();
        father.setNext(husband);
        husband.setNext(son);
        son.setNext(grandson);

        //处理请求
        for (IWomen women : arrayList) {
            father.HandleMessage(women);
        }
    }
}

再设置的传递顺序中,先向父亲请示,不是父亲应该解决的问题,则由父亲传递到丈夫类解决,若不是丈夫类解决的问题,则传递到儿子类解决,最终的结果必然有一个返回,运行结果如下:

---女儿向父亲请示---
女儿的请求是:我要出去逛街
父亲的回答: 同意
---女儿向父亲请示---
女儿的请求是:我要出去逛街
父亲的回答: 同意
---妻子向丈夫请示---
妻子的请求是:我要出去逛街
丈夫的回答: 同意
---女儿向父亲请示---
女儿的请求是:我要出去逛街
父亲的回答: 同意
---母亲向儿子请示---
母亲的请求是:我要出去逛街
儿子的答复是: 同意

责任链模式定义

定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连城一条链,并沿着这条链传递该请求,直到有对象处理它为止。

通用模型

责任链重点是在链上由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果。通用类图如下:
这里写图片描述

链是有多个处理者ConcreteHandler组成,先来看抽象Handler类

处理者抽象
public abstract class Handler {
    private Handler nextHandler;

    /**
     * 处理逻辑
     * @param request
     * @return
     */
    public final Response handleMessage(Request request) {
        Response response = null;
        //判断是否是自己的处理级别
        if (this.getHandlerLevel().equals(request.getRequestLevel())) {
            response = this.echo(request);
        } else {
            //判断是否有下个处理者
            if (this.nextHandler != null) {
                response = this.nextHandler.handleMessage(request);
            } else {
                //没有处理者,逻辑自行处理
            }

        }
        return response;
    }

    /**
     * 设置下个处理者
     * @param _handler
     */
    public void setNext(Handler _handler) {
        this.nextHandler = _handler;
    }

    /**
     * 获取处理者的级别
     * @return
     */
    protected abstract Level getHandlerLevel(); //

    /**
     * 每个处理者都必须实现的处理任务
     * @param request
     * @return
     */
    protected abstract Response echo(Request request);
}

抽象的处理者实现三个职责:一是定义一个请求的处理方法handleMessage,唯一对外开放的方法;二是定义一个链的编排方法setNext,设置下一个处理者;三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别getHandlerLevel和具体的处理任务echo。

注意:在责任链模式中一个请求发送到链中后,前一节点小费部分消息,然后交由后续节点继续处
理,最终可以有处理结果也可以没有处理结果。读者可以不用理会什么纯的,不纯的责任链模式。
同时请读者注意handlerMessage 方法前的final关键字
处理者代码
public class ConcreteHandler1 extends Handler {

    /**
     * 设置自己的级别
     * @return
     */
    protected Level getHandlerLevel() {
        return null;
    }

    /**
     * 完成处理逻辑
     * @param request
     * @return
     */
    protected Response echo(Request request) {
        return null;
    }
}


public class ConcreteHandler2 extends Handler {
    /**
     * 设置自己的级别
     *
     * @return
     */
    protected Level getHandlerLevel() {
        return null;
    }

    /**
     * 完成处理逻辑
     * @param request
     * @return
     */
    protected Response echo(Request request) {
        return null;
    }
}
有关框架代码

/**
 * 定一个请求和处理等级
 */
public class Level {
}
/**
 * 定义请求
 */
public class Request {

    /**
     * 获取请求的等级
     */
    public Level getRequestLevel() {
        return null;
    }
}
/**
 * 处理返回的数据
 */
public class Response {
}
场景类
public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);
        //提交请求,返回结果
        Response response = handler1.handleMessage(new Request());
    }

}

实际上,一般会有一个封装类对责任链模式进行封装,也就是替代CLient类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少了模块间的耦合,提高系统的灵活性。

责任链模式的应用

优点

责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌

缺点:

责任链有两个非常显著的缺点:一是性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是个非常大的问题。二是调试很不方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。

注意事项:

链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handle中设置一个最大节点数量,在setNext方法中判断是否超过其阈值,超过则不允许该链建立,避免无意识的破坏系统性能。

本文摘引自《设计模式之禅(第2版)》
本文源码:https://github.com/527515025/Design_pattern/tree/master/responsibilityChain

展开阅读全文
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值