职责链(Chain of Responsibility)模式在航空货运中的运用实例
设计模式这东西,基本上属于“看懂一瞬间,用会好几年”。只有实际开发中,当某一模式很好的满足了业务需求时,才会有真切的感觉。借用一句《闪电侠》中,绿箭侠教导闪电侠的台词:“不是你碰巧遇到了它(指闪电事故),而是它选择你”。
业务场景:
航空公司内部对于货运单的价格管理,通常会颁发若干类型的运价文件,典型的有:SpotRate(一票一议)、ContractRate(合同运价)、PublicRate(IATA公布运价)等等,一票运单判断该用何种运价时,通常会按一定的顺序在这几类运价中依次匹配查找,如果匹配成功,则直接返回,使用查找结果中的费率做为计算依据。
变化点:
不同的航空公司,内部管理体制不同,支持的运价种类也不同,包括查找运价的顺序也可能略有差异。
目标:
为了能尽量少加班,少改代码,要求系统最好能方便的应对这些变化。职责链模式正是为该类场景而生,园友飞林沙已经详解解读了这一模式,参见其博文:
重温设计模式(三)——职责链模式(chain of responsibility)
类图:

RateCluase 为运价条款基本信息
Airwaybill 为运单基本信息
这二个类的实例,主要做为查找运价的入口参数
RateFinder为统一接口,find方法为查找运价,nextFinder的setter/getter用于指定下一个查找者
XXXRateFinder为具体的实现类,为了简化问题,这里只列了3种基本的实现(实际情况远比这复杂)
代码:
入口参数
/***********************************************************************
* Module: AirwayBill.java
* Author: jimmy
* Purpose: Defines the Class AirwayBill
***********************************************************************/ package murate.test.ratefinder.dto; public class AirwayBill {
/**
* 运单前缀
*
*/
private String awbPre;
/**
* 运单号
*
*/
private String awbNo;
/**
* 始发站
*
*/
private String origin;
/**
* 目的站
*
*/
private String dest;
/**
* 代理人帐号
*
*/
private String agentNumber;
/**
* 品名代码
*
*/
private String commodityCode;
/**
* 特货代码
*
*/
private String specialHandlingCode; public String getAwbPre() {
return awbPre;
} public void setAwbPre(String awbPre) {
this.awbPre = awbPre;
} public String getAwbNo() {
return awbNo;
} public void setAwbNo(String awbNo) {
this.awbNo = awbNo;
} public String getOrigin() {
return origin;
} public void setOrigin(String origin) {
this.origin = origin;
} public String getDest() {
return dest;
} public void setDest(String dest) {
this.dest = dest;
} public String getAgentNumber() {
return agentNumber;
} public void setAgentNumber(String agentNumber) {
this.agentNumber = agentNumber;
} public String getCommodityCode() {
return commodityCode;
} public void setCommodityCode(String commodityCode) {
this.commodityCode = commodityCode;
} public String getSpecialHandlingCode() {
return specialHandlingCode;
} public void setSpecialHandlingCode(String specialHandlingCode) {
this.specialHandlingCode = specialHandlingCode;
} }
/***********************************************************************
* Module: RateCluase.java
* Author: jimmy
* Purpose: Defines the Class RateCluase
***********************************************************************/ package murate.test.ratefinder.dto; /**
* 运价条款
*
* 2014-12-24 杨俊明 0.1
*
*/
public class RateCluase { /**
* 条款Id
*
*/
private Long clauseId; /**
* 条款名称
*
*/
private String clauseName; /**
* 运单前缀
*/
private String awbPre; /**
* 运单号
*/
private String awbNo; /**
* 始发站
*
*/
private String origin; /**
* 目的站
*
*/
private String dest; /**
* 代理人帐号
*
*/
private String agentNumber; /**
* 品名代码
*
*/
private String commodityCode; /**
* 特货代码
*
*/
private String specialHandlingCode; public Long getClauseId() {
return clauseId;
} public void setClauseId(Long clauseId) {
this.clauseId = clauseId;
} public String getClauseName() {
return clauseName;
} public void setClauseName(String clauseName) {
this.clauseName = clauseName;
} public String getOrigin() {
return origin;
} public void setOrigin(String origin) {
this.origin = origin;
} public String getDest() {
return dest;
} public void setDest(String dest) {
this.dest = dest;
} public String getAgentNumber() {
return agentNumber;
} public void setAgentNumber(String agentNumber) {
this.agentNumber = agentNumber;
} public String getCommodityCode() {
return commodityCode;
} public void setCommodityCode(String commodityCode) {
this.commodityCode = commodityCode;
} public String getSpecialHandlingCode() {
return specialHandlingCode;
} public void setSpecialHandlingCode(String specialHandlingCode) {
this.specialHandlingCode = specialHandlingCode;
} public String getAwbPre() {
return awbPre;
} public void setAwbPre(String awbPre) {
this.awbPre = awbPre;
} public String getAwbNo() {
return awbNo;
} public void setAwbNo(String awbNo) {
this.awbNo = awbNo;
} public String toString() {
return clauseName;
} }
接口:
/***********************************************************************
* Module: RateFinder.java
* Author: jimmy
* Purpose: Defines the Interface RateFinder
***********************************************************************/ package murate.test.ratefinder.service; import java.util.List; import murate.test.ratefinder.dto.AirwayBill;
import murate.test.ratefinder.dto.RateCluase; /**
* 运价查找接口
*
*/
public interface RateFinder {
/**
* 查找运价条款
*
* @param airwayBill
* 运单信息
* @param rateClauses
* 运单条款信息
* @return
*/
RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses); RateFinder getNextFinder(); void setNextFinder(RateFinder value); }
3个实现类:
/***********************************************************************
* Module: SpotRateFinder.java
* Author: jimmy
* Purpose: Defines the Class SpotRateFinder
***********************************************************************/ package murate.test.ratefinder.service.impl; import java.util.*; import org.springframework.util.StringUtils; import murate.test.ratefinder.dto.AirwayBill;
import murate.test.ratefinder.dto.RateCluase;
import murate.test.ratefinder.service.RateFinder; /**
* 一票一议运价查找
*
*/
public class SpotRateFinder implements RateFinder { RateFinder nextFinder; public RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses) { for (RateCluase clause : rateClauses) {
// 模拟查找逻辑(只要单号匹配成功,就算通过,仅演示) if (StringUtils.isEmpty(clause.getAwbPre())
|| StringUtils.isEmpty(clause.getAwbNo())
|| StringUtils.isEmpty(airwayBill.getAwbPre())
|| StringUtils.isEmpty(airwayBill.getAwbNo())) {
continue;
}
if (clause.getAwbPre().equals(airwayBill.getAwbPre())
&& clause.getAwbNo().equals(airwayBill.getAwbNo())) {
// 找到了,直接返回
return clause;
}
} // 否则,交给下一个Finder继续查找
return nextFinder.find(airwayBill, rateClauses); } public RateFinder getNextFinder() {
return nextFinder;
} public void setNextFinder(RateFinder value) {
nextFinder = value;
} }
/***********************************************************************
* Module: ContractRateFinder.java
* Author: jimmy
* Purpose: Defines the Class ContractRateFinder
***********************************************************************/ package murate.test.ratefinder.service.impl; import java.util.*; import org.springframework.util.StringUtils; import murate.test.ratefinder.dto.AirwayBill;
import murate.test.ratefinder.dto.RateCluase;
import murate.test.ratefinder.service.RateFinder; /**
* Contract运价查找者
*
*/
public class ContractRateFinder implements RateFinder {
RateFinder nextFinder; public RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses) { for (RateCluase clause : rateClauses) { // 模拟查找逻辑(只要代理人帐号匹配成功,就算通过,仅演示) if (StringUtils.isEmpty(clause.getAgentNumber())
|| StringUtils.isEmpty(clause.getAgentNumber())) {
continue;
} if (clause.getAgentNumber().equals(airwayBill.getAgentNumber())) {
// 找到了,直接返回
return clause;
}
} // 否则,交给下一个Finder继续查找
return nextFinder.find(airwayBill, rateClauses); } public RateFinder getNextFinder() {
return nextFinder;
} public void setNextFinder(RateFinder value) {
nextFinder = value;
} }
/***********************************************************************
* Module: PublicRateFinder.java
* Author: jimmy
* Purpose: Defines the Class PublicRateFinder
***********************************************************************/
package murate.test.ratefinder.service.impl; import java.util.*; import org.springframework.util.StringUtils; import murate.test.ratefinder.dto.AirwayBill;
import murate.test.ratefinder.dto.RateCluase;
import murate.test.ratefinder.service.RateFinder; /**
* 公布运价查找者
*
*/
public class PublicRateFinder implements RateFinder {
RateFinder nextFinder; public RateCluase find(AirwayBill airwayBill, List<RateCluase> rateClauses) { for (RateCluase clause : rateClauses) {
// 模拟查找逻辑(只要始发站、目的站匹配,就算通过,仅演示) if (StringUtils.isEmpty(clause.getOrigin())
|| StringUtils.isEmpty(clause.getDest())
|| StringUtils.isEmpty(airwayBill.getOrigin())
|| StringUtils.isEmpty(airwayBill.getDest())) {
continue;
} if (clause.getOrigin().equals(airwayBill.getOrigin())
&& clause.getDest().equals(airwayBill.getDest())) {
// 找到了,直接返回
return clause;
}
} if (nextFinder == null) {
return null;
} // 否则,交给下一个Finder继续查找
return nextFinder.find(airwayBill, rateClauses); } public RateFinder getNextFinder() {
return nextFinder;
} public void setNextFinder(RateFinder value) {
nextFinder = value;
}
}
注:链的最后一个节点,要有保底处理,即 PublicRateFinder 类42-44 行的处理,否则到“链”的最后一个节点,就会出错了。
配置:
该万能的Spring出场了:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName"> <!-- spotrate->contract->public --> <!-- <bean id="firstFinder" class="murate.test.ratefinder.service.impl.SpotRateFinder">
<property name="nextFinder" ref="contractRateFinder" />
</bean> <bean id="contractRateFinder" class="murate.test.ratefinder.service.impl.ContractRateFinder">
<property name="nextFinder" ref="publicRateFinder" />
</bean> <bean id="publicRateFinder" class="murate.test.ratefinder.service.impl.PublicRateFinder"></bean>
--> <!-- contract->spotrate->public --> <bean id="firstFinder" class="murate.test.ratefinder.service.impl.ContractRateFinder">
<property name="nextFinder" ref="spotRateFinder" />
</bean> <bean id="spotRateFinder" class="murate.test.ratefinder.service.impl.SpotRateFinder">
<property name="nextFinder" ref="publicRateFinder" />
</bean> <bean id="publicRateFinder" class="murate.test.ratefinder.service.impl.PublicRateFinder"></bean> </beans>
测试代码:
package murate.test; import java.util.ArrayList;
import java.util.List; import murate.test.ratefinder.dto.AirwayBill;
import murate.test.ratefinder.dto.RateCluase;
import murate.test.ratefinder.service.RateFinder; import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class RateFinderTest { @Test
public void testFinder() { ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring-beans-test.xml"); RateFinder firstFinder = ctx.getBean("firstFinder", RateFinder.class); List<AirwayBill> awbs = getAwbList();
List<RateCluase> rateCluases = getRateClauses(); for (AirwayBill airwayBill : awbs) {
System.out.println(airwayBill.getAwbPre() + airwayBill.getAwbNo()
+ ":" + firstFinder.find(airwayBill, rateCluases));
} ((ClassPathXmlApplicationContext) ctx).close();
} /**
* 模拟所有运价条款
* @return
*/
private List<RateCluase> getRateClauses() {
List<RateCluase> rateCluases = new ArrayList<RateCluase>(); RateCluase spa = new RateCluase();
spa.setAwbPre("112");
spa.setAwbNo("00000000");
spa.setClauseName("SpotRate测试条款");
rateCluases.add(spa); RateCluase contract = new RateCluase();
contract.setAgentNumber("SHAXYZ");
contract.setClauseName("Contract测试条款 ");
rateCluases.add(contract); RateCluase publicClause = new RateCluase();
publicClause.setOrigin("PVG");
publicClause.setDest("LAX");
publicClause.setClauseName("Public测试条款 ");
rateCluases.add(publicClause); return rateCluases; } /**
* 模拟生成运单数据
* @return
*/
private List<AirwayBill> getAwbList() { //awb1预期匹配Contract条款(或SpotRate,视配置规定的查找顺序)
AirwayBill awb1 = new AirwayBill();
awb1.setAgentNumber("SHAXYZ");
awb1.setAwbPre("112");
awb1.setAwbNo("00000000"); //awb2预期匹配Public条款
AirwayBill awb2 = new AirwayBill();
awb2.setOrigin("PVG");
awb2.setDest("LAX");
awb2.setAwbPre("112");
awb2.setAwbNo("11111111"); //awb3预期匹配SpotRate条款
AirwayBill awb3 = new AirwayBill();
awb3.setAwbPre("112");
awb3.setAwbNo("22222222"); List<AirwayBill> awbList = new ArrayList<AirwayBill>();
awbList.add(awb1);
awbList.add(awb2);
awbList.add(awb3); return awbList; }
}
运行结果:
11200000000:Contract测试条款
11211111111:Public测试条款
11222222222:null
如果把配置中,注释部分和未注释部分对换,即:更改查找顺序,则变成了
11200000000:SpotRate测试条款
11211111111:Public测试条款
11222222222:null
业务扩展:如果以后某航空公司又发明了一种新运价,增加RateFinder的实现类,然后在配置中,把新的处理类,挂到链中的适当位置即可。反之,如果某一类运价,不再使用了,还是修改配置,把这个节点从链中摘除。至于查找顺序的修改,通过nextFinder的配置,形成一条有规则的"链"即可。
职责链(Chain of Responsibility)模式在航空货运中的运用实例的更多相关文章
- C++设计模式实现--职责链(Chain of Responsibility)模式
一. 概述 职责链模式: 使多个对象都有机会处理请求.从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 二. 举个样例 员工要求加薪 ...
- 设计模式C++描述----05.职责链(Chain of Responsibility)模式
一. 概述 职责链模式: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 二. 举个例子 员工要求加薪 ...
- atitit.(设计模式1)--—职责链(chain of responsibility)最佳实践O7 转换日期
atitit.设计模式(1)---职责链模式(chain of responsibility)最佳实践O7 日期转换 1. 需求:::日期转换 1 2. 能够选择的模式: 表格模式,责任链模式 1 3 ...
- 设计模式(十三) 职责链(chain of responsibility)
软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径.设计模式中运用了面向对象编程语言的重要特性:封装.继承.多态,真正领悟设计模式的精髓是可能一个漫长的过程,需要大量实践经验的积累.最 ...
- Java设计模式(14)责任链模式(Chain of Responsibility模式)
Chain of Responsibility定义:Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合, ...
- 设计模式(十四)Chain of Responsibility模式
Chain of Responsibility模式就是当外部请求程序进行某个处理,但程序暂时无法直接决定由哪个对象负责处理时,就需要推卸责任.也就是说,当一个人被要求做什么事时,如果他可以做就自己做, ...
- 《图解设计模式》读书笔记6-2 Chain of Responsibility模式
目录 1. 简介 2. 示例程序 类图 代码 3. 模式的角色和类图 角色 类图 4. 思路拓展 1. 简介 Chain of Responsibility模式是责任链模式,模式的核心就是转移责任.就 ...
- Chain of Responsibility模式
熟悉VC/MFC的都知道,VC是“基于消息,事件驱动”,消息在VC开发中起着举足轻重的作用.MFC提供了消息的处理的链式处理策略,处理消息的请求将沿着预定好的路径依次进行处理.消息的发送者并不知道该消 ...
- 基于.net 职责链来实现 插件模式
插件式的例子 QQ电脑管家,有很多工具列表,点一下工具下载后就可以开始使用了 eclipse ,X Server 等等 插件式的好处 插件降低框架的复杂性,把扩展功能从框架中剥离出来 让第三方有机会来 ...
随机推荐
- mac os下可能是最好的豆瓣电台——diumoo
由于我一直用豆瓣fm听音乐,在网上找了下豆瓣的相关应用,都感觉不是太好, 最后发现一个mac版的app--diumoo! 这个软件看着非常舒服,一点也不占桌面空间,它一直默默在桌面右上角,鼠标划上去会 ...
- 1.9 基础知识——GP2.10 高级别的领导检查(Higher level management)
GP2.10 Review the activities,status,and results of XXX process with highter level management and res ...
- 区别和详解:jQuery中的 $().each()和$.each()/jQuery.each()
1.认识 $().each()遍历当前jQuery对象,并在每一个元素上执行回调函数.其方法内部是通过调用静态方法jQuery.each()来实现的. jQuery.each()是一个通用的遍 ...
- js中的==运算: [''] == false —>true
图1 计算下面表达式的值: [''] == false 首先,两个操作数分别是对象类型.布尔类型.根据图1,需要将布尔类型转为数字类型,而false转为数字的结果是0,所以表达式变为: [''] == ...
- JavaScript Patterns 5.6 Static Members
Public Static Members // constructor var Gadget = function (price) { this.price = price; }; // a sta ...
- 【转】RHadoop实践系列之一:Hadoop环境搭建
RHadoop实践系列之一:Hadoop环境搭建 RHadoop实践系列文章,包含了R语言与Hadoop结合进行海量数据分析.Hadoop主要用来存储海量数据,R语言完成MapReduce 算法,用来 ...
- my_ls
#include<stdio.h> #include<dirent.h> #include<string.h> #include<sys/types.h> ...
- perl操作sybase
设置环境变量 export PERL5LIB=:/redhat/perl/lib64/perl5 安装DBI tar -xzvf DBI-1.631.tar.gz cd DBI-1.631 perl ...
- CURL使用方法详解
php采集神器CURL使用方法详解 作者:佚名 更新时间:2016-10-21 对于做过数据采集的人来说,cURL一定不会陌生.虽然在PHP中有file_get_contents函数可以获取远程 ...
- plsql+绿色版oracle连接远程数据库配置及提示缺少msvcr71.dll解决方法
之前一直用的sqldeveloper连接oracle数据库,这个免费而且也是官方出品,除了体积略大启动略慢外,也没什么不好的.. 一次偶然机会决定试一下plsql,整理一下安装资料,需要本地oracl ...