先看一下类图:
类图非常简单,IHandler上三个决策对象的接口。
//决策对象的接口 public interface IHandler { //处理请求 void HandleMessage(IMan man); } //堆代码 duidaima.com //决策对象:父母 public class Parent implements IHandler { @Override public void HandleMessage(IMan man) { System.out.println("孩子向父母的请求是:" + man.getRequest()); System.out.println("父母的回答是:同意"); } } //决策对象:妻子 public class Wife implements IHandler { @Override public void HandleMessage(IMan man) { System.out.println("丈夫向妻子的请求是:" + man.getRequest()); System.out.println("妻子的回答是:同意"); } } //决策对象:孩子 public class Children implements IHandler{ @Override public void HandleMessage(IMan man) { System.out.println("父亲向孩子的请求是:" + man.getRequest()); System.out.println("孩子的回答是:同意"); } }IMan上男性的接口:
public interface IMan { int getType(); //获取个人状况 String getRequest(); //获取个人请示(这里就简单的用String) } //具体男性对象 public class Man implements IMan { /** * 通过一个int类型去描述男性的个人状况 * 0--幼年 * 1--成年 * 2--年迈 */ private int mType = 0; //请求 private String mRequest = ""; public Man(int type, String request) { this.mType = type; this.mRequest = request; } @Override public int getType() { return mType; } @Override public String getRequest() { return mRequest; } }最后我们看下一下场景类:
public class Client { public static void main(String[] args) { //随机生成几个man Random random = new Random(); ArrayList<IMan> manList = new ArrayList<>(); for (int i = 0; i < 5; i++) { manList.add(new Man(random.nextInt(3), "5块零花钱")); } //定义三个请示对象 IHandler parent = new Parent(); IHandler wife = new Wife(); IHandler children = new Children(); //处理请求 for (IMan man: manList) { switch (man.getType()) { case 0: System.out.println("--------孩子向父母发起请求-------"); parent.HandleMessage(man); break; case 1: System.out.println("--------丈夫向妻子发起请求-------"); wife.HandleMessage(man); break; case 2: System.out.println("--------父亲向孩子发起请求-------"); children.HandleMessage(man); break; default: break; } } } }首先是通过随机方法产生了5个男性的对象,然后看他们是如何就要5块零花钱这件事去请示的,运行结果如下所示:
--------丈夫向妻子发起请求------- 丈夫向妻子的请求是:5块零花钱 妻子的回答是:同意 --------丈夫向妻子发起请求------- 丈夫向妻子的请求是:5块零花钱 妻子的回答是:同意 --------父亲向孩子发起请求------- 父亲向孩子的请求是:5块零花钱 孩子的回答是:同意 --------孩子向父母发起请求------- 孩子向父母的请求是:5块零花钱 父母的回答是:同意 --------丈夫向妻子发起请求------- 丈夫向妻子的请求是:5块零花钱 妻子的回答是:同意发没发现上述的代码是不是有点不舒服,有点别扭,有点想重构它的感觉?那就对了!这段代码有以下几个问题:
丈夫只能向妻子请示吗?丈夫向自己的父母请示了,父母应该做何处理? 我们的程序上可没有体现出来,逻辑失败了!
既然有这么多的问题,那我们要想办法来解决这些问题,我们先来分析一下需求,男性提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父母作出一个决断,而妻子也作出了一个决断,也即是请示传递出去,必然有一个唯一的处理人给出唯一的答复,OK,分析完毕,收工!
重新设计,我们可以抽象成这样一个结构,男性的请求先发送到父亲,父母一看是自己要处理的,就作出回应处理,如果男性已经结婚了,那就要把这个请求转发到妻子来处理,如果男性已经年迈,那就由孩子来处理这个请求,类似于如图所示的顺序处理图。
父母、妻子、孩子每个节点有两个选择:要么承担责任,做出回应;要么把请求转发到后序环节。结构分析得已经很清楚了,那我们看怎么来实现这个功能,类图重新修正,如图 :
从类图上看,三个实现类Parent、Wife、Children只要实现构造函数和父类中的抽象方法 response就可以了,具体由谁处理男性提出的请求,都已经转移到了Handler抽象类中,我们 来看Handler怎么实现:
public abstract class Handler { //处理级别 public static final int PARENT_LEVEL_REQUEST = 0; //父母级别 public static final int WIFE_LEVEL_REQUEST = 1; //妻子级别 public static final int CHILDREN_LEVEL_REQUEST = 2;//孩子级别 private Handler mNextHandler;//下一个责任人 protected abstract int getHandleLevel();//具体责任人的处理级别 protected abstract void response(IMan man);//具体责任人给出的回应 public final void HandleMessage(IMan man) { if (man.getType() == getHandleLevel()) { response(man);//当前责任人可以处理 } else { //当前责任人不能处理,如果有后续处理人,将请求往后传递 if (mNextHandler != null) { mNextHandler.HandleMessage(man); } else { System.out.println("-----没有人可以请示了,不同意该请求-----"); } } } public void setNext(Handler next) { this.mNextHandler = next; } }再看一下具体责任人的实现:Parent、Wife、Children
public class Parent extends Handler{ @Override protected int getHandleLevel() { return Handler.PARENT_LEVEL_REQUEST; } @Override protected void response(IMan man) { System.out.println("----------孩子向父母提出请示----------"); System.out.println(man.getRequest()); System.out.println("父母的回答是:同意"); } } public class Wife extends Handler{ @Override protected int getHandleLevel() { return Handler.WIFE_LEVEL_REQUEST; } @Override protected void response(IMan man) { System.out.println("----------丈夫向妻子提出请示----------"); System.out.println(man.getRequest()); System.out.println("妻子的回答是:同意"); } } public class Children extends Handler{ @Override protected int getHandleLevel() { return Handler.CHILDREN_LEVEL_REQUEST; } @Override protected void response(IMan man) { System.out.println("----------父亲向孩子提出请示----------"); System.out.println(man.getRequest()); System.out.println("孩子的回答是:同意"); } }那么再看一下场景复现: 在Client中设置请求的传递顺序,先向父母请示,不是父母应该解决的问题,则由父母传递到妻子类解决,若不是妻子类解决的问题则传递到孩子类解决,最终的结果必然有一个返回,其运行结果如下所示:
----------孩子向父母提出请示---------- 15块零花钱 父母的回答是:同意 ----------丈夫向妻子提出请示---------- 15块零花钱 妻子的回答是:同意 ----------父亲向孩子提出请示---------- 15块零花钱 孩子的回答是:同意 ----------丈夫向妻子提出请示---------- 15块零花钱 妻子的回答是:同意 ----------父亲向孩子提出请示---------- 15块零花钱 孩子的回答是:同意结果也正确,业务调用类Client也不用去做判断到底是需要谁去处理,而且Handler抽象类的子类可以继续增加下去,只需要扩展传递链而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。在这种模式就是责任链模式。
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。) 责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请 求,并返回相应的结果,其通用类图如图所示
最后总结一下,责任链的模版:包含四个对象,Handler,Request,Level,Response:
public class Request { //请求的等级 public Level getRequestLevel(){ return null; } } public class Level { //请求级别 } public class Response { //处理者返回的数据 } //抽象处理者 public abstract class Handler { private Handler mNextHandler; //每个处理者都必须对请求做出处理 public final Response handleMessage(Request request) { Response response = null; if (getHandlerLevel().equals(request.getRequestLevel())) { //是自己处理的级别,自己处理 response = echo(request); } else { //不是自己处理的级别,交给下一个处理者 if (mNextHandler != null) { response = mNextHandler.echo(request); } else { //没有处理者能处理,业务自行处理 } } return response; } public void setNext(Handler next) { this.mNextHandler = next; } @NotNull protected abstract Level getHandlerLevel(); protected abstract Response echo(Request request); }实际应用
//抽象处理者 abstract class AbsDialog(private val context: Context) { private var nextDialog: AbsDialog? = null //优先级 abstract fun getPriority(): Int //是否需要展示 abstract fun needShownDialog(): Boolean fun setNextDialog(dialog: AbsDialog?) { nextDialog = dialog } open fun showDialog() { //这里的逻辑,我们就简单点,具体逻辑根据业务而定 if (needShownDialog()) { show() } else { nextDialog?.showDialog() } } protected abstract fun show() // Sp存储, 记录是否已经展示过 open fun needShow(key: String): Boolean { val sp: SharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE) return sp.getBoolean(key, true) } open fun setShown(key: String, show: Boolean) { val sp: SharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE) sp.edit().putBoolean(key, !show).apply() } companion object { const val LOG_TAG = "Dialog" const val SP_NAME = "dialog" const val POLICY_DIALOG_KEY = "policy_dialog" const val AD_DIALOG_KEY = "ad_dialog" const val PRAISE_DIALOG_KEY = "praise_dialog" } } /** * 模拟 隐私政策弹窗 * */ class PolicyDialog(context: Context) : AbsDialog(context) { override fun getPriority(): Int = 0 override fun needShownDialog(): Boolean { // 这里可以根据业务逻辑判断是否需要显示弹窗,如接口控制等等 // 这里通过Sp存储来模拟 return needShow(POLICY_DIALOG_KEY) } override fun show() { Log.d(LOG_TAG, "显示隐私政策弹窗") setShown(POLICY_DIALOG_KEY, true) //记录已经显示过 } } /** * 模拟 广告弹窗 * */ class AdDialog(private val context: Context) : AbsDialog(context) { private val ad = DialogData(1, "XX广告弹窗") // 模拟广告数据 override fun getPriority(): Int = 1 override fun needShownDialog(): Boolean { // 广告数据通过接口获取,广告id应该是唯一的,所以根据id保持sp return needShow(AD_DIALOG_KEY + ad.id) } override fun show() { Log.d(LOG_TAG, "显示广告弹窗:${ad.name}") setShown(AD_DIALOG_KEY + ad.id, true) } } /** * 模拟 好评弹窗 * */ class PraiseDialog(context: Context) : AbsDialog(context) { override fun getPriority(): Int = 2 override fun needShownDialog(): Boolean { // 这里可以根据业务逻辑判断是否需要显示弹窗,如用户使用7天等 // 这里通过Sp存储来模拟 return needShow(PRAISE_DIALOG_KEY) } override fun show() { Log.d(LOG_TAG, "显示好评弹窗") setShown(PRAISE_DIALOG_KEY, true) } } //模拟打开app val dialogs = mutableListOf<AbsDialog>() dialogs.add(PolicyDialog(this)) dialogs.add(PraiseDialog(this)) dialogs.add(AdDialog(this)) //根据优先级排序 dialogs.sortBy { it.getPriority() } //创建链条 for (i in 0 until dialogs.size - 1) { dialogs[i].setNextDialog(dialogs[i + 1]) } dialogs[0].showDialog()第一次打开