职责链(Chain of Responsibility)模式在航空货运中的运用实例

设计模式这东西,基本上属于“看懂一瞬间,用会好几年”。只有实际开发中,当某一模式很好的满足了业务需求时,才会有真切的感觉。借用一句《闪电侠》中,绿箭侠教导闪电侠的台词:“不是你碰巧遇到了它(指闪电事故),而是它选择你”。

业务场景:

航空公司内部对于货运单的价格管理,通常会颁发若干类型的运价文件,典型的有:SpotRate(一票一议)、ContractRate(合同运价)、PublicRate(IATA公布运价)等等,一票运单判断该用何种运价时,通常会按一定的顺序在这几类运价中依次匹配查找,如果匹配成功,则直接返回,使用查找结果中的费率做为计算依据。

变化点:

不同的航空公司,内部管理体制不同,支持的运价种类也不同,包括查找运价的顺序也可能略有差异。

目标:

为了能尽量少加班,少改代码,要求系统最好能方便的应对这些变化。职责链模式正是为该类场景而生,园友飞林沙已经详解解读了这一模式,参见其博文:

重温设计模式(三)——职责链模式(chain of responsibility)

 

类图:

RateCluase 为运价条款基本信息

Airwaybill 为运单基本信息

这二个类的实例,主要做为查找运价的入口参数

RateFinder为统一接口,find方法为查找运价,nextFinder的setter/getter用于指定下一个查找者

XXXRateFinder为具体的实现类,为了简化问题,这里只列了3种基本的实现(实际情况远比这复杂)

 

代码:

入口参数

  1 /***********************************************************************
  2  * Module:  AirwayBill.java
  3  * Author:  jimmy
  4  * Purpose: Defines the Class AirwayBill
  5  ***********************************************************************/
  6
  7 package murate.test.ratefinder.dto;
  8
  9 public class AirwayBill {
 10     /**
 11      * 运单前缀
 12      *
 13      */
 14     private String awbPre;
 15     /**
 16      * 运单号
 17      *
 18      */
 19     private String awbNo;
 20     /**
 21      * 始发站
 22      *
 23      */
 24     private String origin;
 25     /**
 26      * 目的站
 27      *
 28      */
 29     private String dest;
 30     /**
 31      * 代理人帐号
 32      *
 33      */
 34     private String agentNumber;
 35     /**
 36      * 品名代码
 37      *
 38      */
 39     private String commodityCode;
 40     /**
 41      * 特货代码
 42      *
 43      */
 44     private String specialHandlingCode;
 45
 46     public String getAwbPre() {
 47         return awbPre;
 48     }
 49
 50     public void setAwbPre(String awbPre) {
 51         this.awbPre = awbPre;
 52     }
 53
 54     public String getAwbNo() {
 55         return awbNo;
 56     }
 57
 58     public void setAwbNo(String awbNo) {
 59         this.awbNo = awbNo;
 60     }
 61
 62     public String getOrigin() {
 63         return origin;
 64     }
 65
 66     public void setOrigin(String origin) {
 67         this.origin = origin;
 68     }
 69
 70     public String getDest() {
 71         return dest;
 72     }
 73
 74     public void setDest(String dest) {
 75         this.dest = dest;
 76     }
 77
 78     public String getAgentNumber() {
 79         return agentNumber;
 80     }
 81
 82     public void setAgentNumber(String agentNumber) {
 83         this.agentNumber = agentNumber;
 84     }
 85
 86     public String getCommodityCode() {
 87         return commodityCode;
 88     }
 89
 90     public void setCommodityCode(String commodityCode) {
 91         this.commodityCode = commodityCode;
 92     }
 93
 94     public String getSpecialHandlingCode() {
 95         return specialHandlingCode;
 96     }
 97
 98     public void setSpecialHandlingCode(String specialHandlingCode) {
 99         this.specialHandlingCode = specialHandlingCode;
100     }
101
102 }

View Code

  1 /***********************************************************************
  2  * Module:  RateCluase.java
  3  * Author:  jimmy
  4  * Purpose: Defines the Class RateCluase
  5  ***********************************************************************/
  6
  7 package murate.test.ratefinder.dto;
  8
  9 /**
 10  * 运价条款
 11  *
 12  * 2014-12-24 杨俊明 0.1
 13  *
 14  */
 15 public class RateCluase {
 16
 17     /**
 18      * 条款Id
 19      *
 20      */
 21     private Long clauseId;
 22
 23     /**
 24      * 条款名称
 25      *
 26      */
 27     private String clauseName;
 28
 29     /**
 30      * 运单前缀
 31      */
 32     private String awbPre;
 33
 34     /**
 35      * 运单号
 36      */
 37     private String awbNo;
 38
 39     /**
 40      * 始发站
 41      *
 42      */
 43     private String origin;
 44
 45     /**
 46      * 目的站
 47      *
 48      */
 49     private String dest;
 50
 51     /**
 52      * 代理人帐号
 53      *
 54      */
 55     private String agentNumber;
 56
 57     /**
 58      * 品名代码
 59      *
 60      */
 61     private String commodityCode;
 62
 63     /**
 64      * 特货代码
 65      *
 66      */
 67     private String specialHandlingCode;
 68
 69     public Long getClauseId() {
 70         return clauseId;
 71     }
 72
 73     public void setClauseId(Long clauseId) {
 74         this.clauseId = clauseId;
 75     }
 76
 77     public String getClauseName() {
 78         return clauseName;
 79     }
 80
 81     public void setClauseName(String clauseName) {
 82         this.clauseName = clauseName;
 83     }
 84
 85     public String getOrigin() {
 86         return origin;
 87     }
 88
 89     public void setOrigin(String origin) {
 90         this.origin = origin;
 91     }
 92
 93     public String getDest() {
 94         return dest;
 95     }
 96
 97     public void setDest(String dest) {
 98         this.dest = dest;
 99     }
100
101     public String getAgentNumber() {
102         return agentNumber;
103     }
104
105     public void setAgentNumber(String agentNumber) {
106         this.agentNumber = agentNumber;
107     }
108
109     public String getCommodityCode() {
110         return commodityCode;
111     }
112
113     public void setCommodityCode(String commodityCode) {
114         this.commodityCode = commodityCode;
115     }
116
117     public String getSpecialHandlingCode() {
118         return specialHandlingCode;
119     }
120
121     public void setSpecialHandlingCode(String specialHandlingCode) {
122         this.specialHandlingCode = specialHandlingCode;
123     }
124
125     public String getAwbPre() {
126         return awbPre;
127     }
128
129     public void setAwbPre(String awbPre) {
130         this.awbPre = awbPre;
131     }
132
133     public String getAwbNo() {
134         return awbNo;
135     }
136
137     public void setAwbNo(String awbNo) {
138         this.awbNo = awbNo;
139     }
140
141     public String toString() {
142         return clauseName;
143     }
144
145 }

View Code

接口:

 1 /***********************************************************************
 2  * Module:  RateFinder.java
 3  * Author:  jimmy
 4  * Purpose: Defines the Interface RateFinder
 5  ***********************************************************************/
 6
 7 package murate.test.ratefinder.service;
 8
 9 import java.util.List;
10
11 import murate.test.ratefinder.dto.AirwayBill;
12 import murate.test.ratefinder.dto.RateCluase;
13
14 /**
15  * 运价查找接口
16  *
17  */
18 public interface RateFinder {
19     /**
20      * 查找运价条款
21      *
22      * @param airwayBill
23      *            运单信息
24      * @param rateClauses
25      *            运单条款信息
26      * @return
27      */
28     RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses);
29
30     RateFinder getNextFinder();
31
32     void setNextFinder(RateFinder value);
33
34 }

View Code

3个实现类:

 1 /***********************************************************************
 2  * Module:  SpotRateFinder.java
 3  * Author:  jimmy
 4  * Purpose: Defines the Class SpotRateFinder
 5  ***********************************************************************/
 6
 7 package murate.test.ratefinder.service.impl;
 8
 9 import java.util.*;
10
11 import org.springframework.util.StringUtils;
12
13 import murate.test.ratefinder.dto.AirwayBill;
14 import murate.test.ratefinder.dto.RateCluase;
15 import murate.test.ratefinder.service.RateFinder;
16
17 /**
18  * 一票一议运价查找
19  *
20  */
21 public class SpotRateFinder implements RateFinder {
22
23     RateFinder nextFinder;
24
25     public RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses) {
26
27         for (RateCluase clause : rateClauses) {
28             // 模拟查找逻辑(只要单号匹配成功,就算通过,仅演示)
29
30             if (StringUtils.isEmpty(clause.getAwbPre())
31                     || StringUtils.isEmpty(clause.getAwbNo())
32                     || StringUtils.isEmpty(airwayBill.getAwbPre())
33                     || StringUtils.isEmpty(airwayBill.getAwbNo())) {
34                 continue;
35             }
36             if (clause.getAwbPre().equals(airwayBill.getAwbPre())
37                     && clause.getAwbNo().equals(airwayBill.getAwbNo())) {
38                 // 找到了,直接返回
39                 return clause;
40             }
41         }
42
43         // 否则,交给下一个Finder继续查找
44         return nextFinder.find(airwayBill, rateClauses);
45
46     }
47
48     public RateFinder getNextFinder() {
49         return nextFinder;
50     }
51
52     public void setNextFinder(RateFinder value) {
53         nextFinder = value;
54     }
55
56 }

View Code

 1 /***********************************************************************
 2  * Module:  ContractRateFinder.java
 3  * Author:  jimmy
 4  * Purpose: Defines the Class ContractRateFinder
 5  ***********************************************************************/
 6
 7 package murate.test.ratefinder.service.impl;
 8
 9 import java.util.*;
10
11 import org.springframework.util.StringUtils;
12
13 import murate.test.ratefinder.dto.AirwayBill;
14 import murate.test.ratefinder.dto.RateCluase;
15 import murate.test.ratefinder.service.RateFinder;
16
17 /**
18  * Contract运价查找者
19  *
20  */
21 public class ContractRateFinder implements RateFinder {
22     RateFinder nextFinder;
23
24     public RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses) {
25
26         for (RateCluase clause : rateClauses) {
27
28             // 模拟查找逻辑(只要代理人帐号匹配成功,就算通过,仅演示)
29
30             if (StringUtils.isEmpty(clause.getAgentNumber())
31                     || StringUtils.isEmpty(clause.getAgentNumber())) {
32                 continue;
33             }
34
35             if (clause.getAgentNumber().equals(airwayBill.getAgentNumber())) {
36                 // 找到了,直接返回
37                 return clause;
38             }
39         }
40
41         // 否则,交给下一个Finder继续查找
42         return nextFinder.find(airwayBill, rateClauses);
43
44     }
45
46     public RateFinder getNextFinder() {
47         return nextFinder;
48     }
49
50     public void setNextFinder(RateFinder value) {
51         nextFinder = value;
52     }
53
54 }

View Code

 1 /***********************************************************************
 2  * Module:  PublicRateFinder.java
 3  * Author:  jimmy
 4  * Purpose: Defines the Class PublicRateFinder
 5  ***********************************************************************/
 6 package murate.test.ratefinder.service.impl;
 7
 8 import java.util.*;
 9
10 import org.springframework.util.StringUtils;
11
12 import murate.test.ratefinder.dto.AirwayBill;
13 import murate.test.ratefinder.dto.RateCluase;
14 import murate.test.ratefinder.service.RateFinder;
15
16 /**
17  * 公布运价查找者
18  *
19  */
20 public class PublicRateFinder implements RateFinder {
21     RateFinder nextFinder;
22
23     public RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses) {
24
25         for (RateCluase clause : rateClauses) {
26             // 模拟查找逻辑(只要始发站、目的站匹配,就算通过,仅演示)
27
28             if (StringUtils.isEmpty(clause.getOrigin())
29                     || StringUtils.isEmpty(clause.getDest())
30                     || StringUtils.isEmpty(airwayBill.getOrigin())
31                     || StringUtils.isEmpty(airwayBill.getDest())) {
32                 continue;
33             }
34
35             if (clause.getOrigin().equals(airwayBill.getOrigin())
36                     && clause.getDest().equals(airwayBill.getDest())) {
37                 // 找到了,直接返回
38                 return clause;
39             }
40         }
41
42         if (nextFinder == null) {
43             return null;
44         }
45
46         // 否则,交给下一个Finder继续查找
47         return nextFinder.find(airwayBill, rateClauses);
48
49     }
50
51     public RateFinder getNextFinder() {
52         return nextFinder;
53     }
54
55     public void setNextFinder(RateFinder value) {
56         nextFinder = value;
57     }
58 }

View Code

注:链的最后一个节点,要有保底处理,即 PublicRateFinder 类42-44 行的处理,否则到“链”的最后一个节点,就会出错了。

 

配置:

该万能的Spring出场了:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 4     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="
 7      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
 8      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 9      http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
10      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
11      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
12     default-autowire="byName">
13
14     <!-- spotrate->contract->public -->
15
16     <!-- <bean id="firstFinder" class="murate.test.ratefinder.service.impl.SpotRateFinder">
17         <property name="nextFinder" ref="contractRateFinder" />
18     </bean>
19
20     <bean id="contractRateFinder" class="murate.test.ratefinder.service.impl.ContractRateFinder">
21         <property name="nextFinder" ref="publicRateFinder" />
22     </bean>
23
24     <bean id="publicRateFinder" class="murate.test.ratefinder.service.impl.PublicRateFinder"></bean>
25  -->
26
27     <!-- contract->spotrate->public -->
28
29      <bean id="firstFinder" class="murate.test.ratefinder.service.impl.ContractRateFinder">
30         <property name="nextFinder" ref="spotRateFinder" />
31     </bean>
32
33     <bean id="spotRateFinder" class="murate.test.ratefinder.service.impl.SpotRateFinder">
34         <property name="nextFinder" ref="publicRateFinder" />
35     </bean>
36
37     <bean id="publicRateFinder" class="murate.test.ratefinder.service.impl.PublicRateFinder"></bean>
38
39
40 </beans>

View Code

 

测试代码:

 1 package murate.test;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5
 6 import murate.test.ratefinder.dto.AirwayBill;
 7 import murate.test.ratefinder.dto.RateCluase;
 8 import murate.test.ratefinder.service.RateFinder;
 9
10 import org.junit.Test;
11 import org.springframework.context.ApplicationContext;
12 import org.springframework.context.support.ClassPathXmlApplicationContext;
13
14 public class RateFinderTest {
15
16     @Test
17     public void testFinder() {
18
19         ApplicationContext ctx = new ClassPathXmlApplicationContext(
20                 "spring-beans-test.xml");
21
22         RateFinder firstFinder = ctx.getBean("firstFinder", RateFinder.class);
23
24         List<AirwayBill> awbs = getAwbList();
25         List<RateCluase> rateCluases = getRateClauses();
26
27         for (AirwayBill airwayBill : awbs) {
28             System.out.println(airwayBill.getAwbPre() + airwayBill.getAwbNo()
29                     + ":" + firstFinder.find(airwayBill, rateCluases));
30         }
31
32         ((ClassPathXmlApplicationContext) ctx).close();
33     }
34
35     /**
36      * 模拟所有运价条款
37      * @return
38      */
39     private List<RateCluase> getRateClauses() {
40         List<RateCluase> rateCluases = new ArrayList<RateCluase>();
41
42         RateCluase spa = new RateCluase();
43         spa.setAwbPre("112");
44         spa.setAwbNo("00000000");
45         spa.setClauseName("SpotRate测试条款");
46         rateCluases.add(spa);
47
48         RateCluase contract = new RateCluase();
49         contract.setAgentNumber("SHAXYZ");
50         contract.setClauseName("Contract测试条款 ");
51         rateCluases.add(contract);
52
53         RateCluase publicClause = new RateCluase();
54         publicClause.setOrigin("PVG");
55         publicClause.setDest("LAX");
56         publicClause.setClauseName("Public测试条款 ");
57         rateCluases.add(publicClause);
58
59         return rateCluases;
60
61     }
62
63     /**
64      * 模拟生成运单数据
65      * @return
66      */
67     private List<AirwayBill> getAwbList() {
68
69         //awb1预期匹配Contract条款(或SpotRate,视配置规定的查找顺序)
70         AirwayBill awb1 = new AirwayBill();
71         awb1.setAgentNumber("SHAXYZ");
72         awb1.setAwbPre("112");
73         awb1.setAwbNo("00000000");
74
75         //awb2预期匹配Public条款
76         AirwayBill awb2 = new AirwayBill();
77         awb2.setOrigin("PVG");
78         awb2.setDest("LAX");
79         awb2.setAwbPre("112");
80         awb2.setAwbNo("11111111");
81
82         //awb3预期匹配SpotRate条款
83         AirwayBill awb3 = new AirwayBill();
84         awb3.setAwbPre("112");
85         awb3.setAwbNo("22222222");
86
87         List<AirwayBill> awbList = new ArrayList<AirwayBill>();
88         awbList.add(awb1);
89         awbList.add(awb2);
90         awbList.add(awb3);
91
92         return awbList;
93
94     }
95 }

View Code

运行结果:

11200000000:Contract测试条款
11211111111:Public测试条款
11222222222:null

如果把配置中,注释部分和未注释部分对换,即:更改查找顺序,则变成了

11200000000:SpotRate测试条款
11211111111:Public测试条款
11222222222:null

 

业务扩展:如果以后某航空公司又发明了一种新运价,增加RateFinder的实现类,然后在配置中,把新的处理类,挂到链中的适当位置即可。反之,如果某一类运价,不再使用了,还是修改配置,把这个节点从链中摘除。至于查找顺序的修改,通过nextFinder的配置,形成一条有规则的"链"即可。

 

时间: 2024-10-31 09:04:15

职责链(Chain of Responsibility)模式在航空货运中的运用实例的相关文章

Java设计模式之责任链模式(Chain of Responsibility模式)介绍_java

Chain of Responsibility定义:Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一共同点是在他们之间传递request.也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递到C类处理,就这样象一个链条(chain)一样传递下去. 如何使用责任链模式虽然这一段是如何使用CoR,但是也是演示什么是CoR. 有一个Handler接口: 复制代码

设计模式中的模板方法模式在Ruby中的应用实例两则_ruby专题

实例一今天你还是像往常一样来上班,一如既往地开始了你的编程工作. 项目经理告诉你,今天想在服务器端增加一个新功能,希望写一个方法,能对Book对象进行处理,将Book对象的所有字段以XML格式进行包装,这样以后可以方便与客户端进行交互.并且在包装开始前和结束后要打印日志,这样方便调试和问题定位. 没问题!你觉得这个功能简直是小菜一碟,非常自信地开始写起代码. Book对象代码如下: class Book attr_accessor :book_name, :pages, :price, :aut

设计模式学习笔记(十九)—Chain of Responsibility职责链模式

由于本人水平有限,写出来的东西也许对初学者有所帮助.如果不小心哪位大侠看了不要见笑,哪里有不正确的地方还请批评指正.好了不说废话了. Chain of Responsibility模式定义: 为了避免请求的发送者和接收者之间的耦合关系,使多个接受对象都有机会处理请求.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 我的理解: 在不止一个对象可以处理客户端请求的时候,为了使每个对象都有处理请求的机会,把这些对象顺序地串联起来形成一个链,每个被串联的对象都有一个指向下一个对

职责链模式(chain of responsibility)

原文地址 一. 写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式. 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理.所以希望各位多多指教. 二. 什么是链 文章伊始,先让我们了解这个最基本的概念,什么是链. 我给链下了这样的定义: 链是一系列节点的集合. 链的各节点可灵活拆分再重组. 三. 何为职责链 职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链

设计模式 ( 十二 ) 职责链模式(Chain of Responsibility)(对象行为)

 设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述        你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象).至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与官员之间耦合在一起,让多个(部门)对象都有可能接收请求,将这些(部门)对象连接成一条链,并且沿着这条链传递请求,直到有(部门)对象处理它为止. 例子1:js的事件浮升机制 例子2: 2.问题

第22章 职责链模式(Chain of Responsibility)

原文 第22章 职责链模式(Chain of Responsibility) 职责链模式        导读:职责链模式是一个既简单又复杂的设计模式,刚开始学习这个设计模式的时候光示例都看了好几遍.就为了理清里面的逻辑走向.是个值得慢慢品味的设计模式        概述:        使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止.          结构图:          代码举例:公司请假

php Chain of Responsibility 职责链模式

比如一颗原子弹投下的瞬间,在杀伤范围内的部队或者建筑都会减少血,但是随着距离中心点的远近,受损程度是不同的,而且不同的兵种和建筑受损情况是不同的. 待解决的问题:原子弹投下的瞬间,将杀伤的处理分别交给杀伤范围内的部队或者建筑自己的方法处理. 思路:建立一个接口,让所有的部队或者建筑实现.   职责链模式(Chain of Responsibility)示例:  代码如下 复制代码 <?php //被原子弹攻击的接口 interface NuclearAttacked {     //处理被原子弹

php设计模式 Chain Of Responsibility (职责链模式)

复制代码 代码如下: <?php /** * 职责链模式 * * 为解除请求的发送者和接收者之间的耦合,而使用多个对象都用机会处理这个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它 * */ abstract class Handler { protected $_handler = null; public function setSuccessor($handler) { $this->_handler = $handler; } abstract functio

重温设计模式(三)——职责链模式(chain of responsibility)

一.写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式. 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理.所以希望各位多多指教. 二.什么是链 文章伊始,先让我们了解这个最基本的概念,什么是链. 我给链下了这样的定义: 1.链是一系列节点的集合. 2.链的各节点可灵活拆分再重组. 三.何为职责链 职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着