前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >职责链(Chain of Responsibility)模式在航空货运中的运用实例

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

作者头像
菩提树下的杨过
发布2018-01-19 11:06:55
7950
发布2018-01-19 11:06:55
举报

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

业务场景:

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

变化点:

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

目标:

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

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

类图:

RateCluase 为运价条款基本信息

Airwaybill 为运单基本信息

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

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

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

代码:

入口参数

代码语言:javascript
复制
  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 }
代码语言:javascript
复制
  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 }

接口:

代码语言:javascript
复制
 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 }

3个实现类:

代码语言:javascript
复制
 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 }
代码语言:javascript
复制
 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 }
代码语言:javascript
复制
 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 }

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

配置:

该万能的Spring出场了:

代码语言:javascript
复制
 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>

测试代码:

代码语言:javascript
复制
 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 }

运行结果:

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2014-12-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档