责任链模式的定义是:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。(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.) 责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果,其通用类图如图:  责任链模式的核心在“链”上,“链”是由多个处理者 ConcreteHandler 组成的,当请求来临时,则会走访这个“链”,根据 Client 的请求,要么找到合适的 ConcreteHandler ,要么找不到合适的 ConcreteHandler ,就当作异常处理。 例如,对于一个球员,他可能处于休赛期、赛季前、赛前、比赛中。对应的,在休赛期,会要求经纪人安排商业活动;在赛季前,会要求私人训练师安排恢复训练;在赛前被教练安排战术;在比赛中被队长安排场上位置等,可以用一个 Player 类来模拟运动员,这个运动员在休赛期和赛季前会要求经纪人和训练师安排今天的日程,在赛前和比赛中则要向教练和队长请示战术安排和场上位置安排,代码如下: ```java interface IPlayer { // 获得当前状态 public int getState(); // 询问接下来做什么 public String getRequest(); } class Player implements IPlayer { /** * 通过 int 类型参数描述运动员当前状态 * 1--休赛期 * 2--恢复训练 * 3--赛前准备 * 4--比赛中 */ private int state = 0; public String request = ""; public Player(int _state) { this.state = _state; } @Override public int getState() { return state; } @Override public String getRequest() { switch (this.state) { case 1: case 2: this.request = "今天有什么安排?"; break; case 3: case 4: this.request = "安排我做什么?"; break; default: break; } return this.request; } } ``` 在非赛期,经纪人会为运动员安排商业活动,训练师会为安排训练内容;回到球队,在比赛前,要接受教练安排的战术,在比赛中,要接受场上队长安排的位置,这些也可以在对应的类中分别定义: ```java class Agent extends Responser { public Agent() { super(Responser.AGENT_LEVEL_REQUEST); } protected void response(IPlayer player) { System.out.println("--------运动员向经纪人请示--------"); System.out.println(player.getRequest()); int number = (int)Math.random() % 2; if (number == 0) { System.out.println("经纪人回复:今天有赞助商活动\n"); } else { System.out.println("经纪人回复:今天暂时没有活动\n"); } } } class PrivateTrainer extends Responser { public PrivateTrainer() { super(Responser.PRIVATE_TRAINER_LEVEL_REQUEST); } protected void response(IPlayer player) { System.out.println("--------运动员向训练师请示--------"); System.out.println(player.getRequest()); Random random = new Random(); int number = random.nextInt(3); switch (number) { case 0: System.out.println("训练师回复:今天安排重训\n"); break; case 1: System.out.println("训练师回复:今天安排有球训练\n"); break; case 2: System.out.println("训练师回复:今天安排反应灵敏训练\n"); break; default: break; } } } class Coach extends Responser { public Coach() { super(Responser.COACH_LEVEL_REQUEST); } protected void response(IPlayer player) { System.out.println("--------运动员向教练请示--------"); System.out.println(player.getRequest()); System.out.println("教练员的答复是:执行战术\n"); } } class Caption extends Responser { public Caption() { super(Responser.CAPTION_LEVEL_REQUEST); } protected void response(IPlayer player) { System.out.println("--------运动员向场上队长请示--------"); System.out.println(player.getRequest()); Random random = new Random(); int number = random.nextInt(2); if (number == 0) { System.out.println("场上队长的答复是:持球主攻\n"); } else { System.out.println("场上队长的答复是:无球跑位\n"); } } } ``` 责任链模式的核心就在于,Responser 需要用一个“链”来维护这一系列安排和要求,Responser 也需要明确自己在“链”中的什么位置,能根据 Player 当前所处的状态在“链”中匹配到合适的人,也就是 Responser 来处理 Player 的请求。这些代码可以在 Responser 类中以模板方法模式中定义出来: ```java abstract class Responser { // 全局定义处理级别 public final static int AGENT_LEVEL_REQUEST = 1; public final static int PRIVATE_TRAINER_LEVEL_REQUEST = 2; public final static int COACH_LEVEL_REQUEST = 3; public final static int CAPTION_LEVEL_REQUEST = 4; // 当前 Responser 能处理的级别 private int level = 0; public Responser(int _level) { this.level = _level; } // 责任链 private Responser nextResponser; public void setNext(Responser responser) { this.nextResponser = responser; } public final void handleMessage(IPlayer player) { if (player.getState() == this.level) { // 找到合适的 Responser this.response(player); } else { if (this.nextResponser != null) { // 走访后续的 Responser this.nextResponser.handleMessage(player); } else { // 没有合适的 Responser System.out.println("---没人回应,应该要退役了---\n"); } } } protected abstract void response(IPlayer player); } ``` 最后,在场景类中设置好责任链,需要确定找到谁做日程安排,或找到谁做请示,就很容易了。 ```java public class Client { public static void main(String[] args) { // 随机挑选几个运动员 Random rand = new Random(); ArrayList<IPlayer> arrayList = new ArrayList<IPlayer>(); for (int i = 0; i < 5; i++) { arrayList.add(new Player(rand.nextInt(3) + 1)); } // 定义四个请示对象 Responser agent = new Agent(); Responser privateTrainer = new PrivateTrainer(); Responser coach = new Coach(); Responser caption = new Caption(); // 设置责任链 agent.setNext(privateTrainer); privateTrainer.setNext(coach); coach.setNext(caption); for (IPlayer player : arrayList) { agent.handleMessage(player); } } } ``` 这个例子在责任链模式中融合了模板方法模式,Responser 子类只需要关注自己的业务逻辑,不需要关注 Request 的处理过程;责任链则由抽象类 Responser 来实现,这符合单一职责原则和迪米特法则。作为 Requester,可以不用知道到底是需要谁来处理的,例如上面的 Client 中,统统交给 Agent 来处理,这也是责任链模式的一大特点。 在实际应用中,一般会有一个封装类对责任模式进行封装,也就是替代 Client 类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少模块间的耦合,提高系统的灵活性。 ## 责任链模式的优点 责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。 ## 责任链模式的缺点 责任链有两个非常显著的缺点:一是性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。二是调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。 ## 责任链模式的注意事项 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在 Responser 中设置一个最大节点数量,在 setResponser 方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。 责任链模式也可以作为一种补救模式来使用,在项目开发过程中,一开始需求确认是这样的:一个请求(如银行客户存款的币种),一个处理者(只处理人民币),但是随着业务的发展(改革开放了嘛,还要处理美元、日元等),处理者的数量和类型都有所增加,那这时候就可以在第一个处理者后面建立一个链,也就是责任链来处理请求,如果是人民币,好,还是第一个业务逻辑来处理;如果是美元,好,传递到第二个业务逻辑来处理;日元、欧元……这些都不用在对原有的业务逻辑产生很大改变,通过扩展实现类就可以很好地解决这些需求变更的问题。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up