职责链(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 等等 插件式的好处 插件降低框架的复杂性,把扩展功能从框架中剥离出来 让第三方有机会来 ...
随机推荐
- WPF学习之路(十一)布局(续)
布局实际上是一个Slot模型,其中每个父对象分配给子对象一个Slot,子对象可以自由占用Slot中的空间,通过Margin\VerticalAlignment\HorizontalAlignment控 ...
- INBOUND_CONNECT_TIMEOUT与SQLNET.INBOUND_CONNECT_TIMEOUT小结
关于sqlnet.ora的参数SQLNET.INBOUND_CONNECT_TIMEOUT,它表示等待用户认证超时的时间,单位是秒,缺省值是60秒,如果用户认证超时了,服务器日志alert.log显示 ...
- NGINX高性能Web服务器详解(读书笔记)
原文地址:NGINX高性能Web服务器详解(读书笔记) 作者:夏寥寥 第4章 Nginx服务器的高级配置 4.1 针对IPv4的内核7个参数的配置优化 说明:我们可以将这些内核参数的值追加到Linu ...
- Java堆、栈和常量池
摘录自 http://www.cnblogs.com/xiohao/p/4296088.html 1. 栈(stack)与堆(heap)都是Java用来在RAM中存放数据的地方.与C++不同,Java ...
- 0022 Java学习笔记-面向对象-继承、多态、组合
继承的特点 单继承:每个子类最多只有一个直接父类,注意是直接父类,间接父类个数不限 注意父类的概念:A-->B-->C-->D,在这里,ABC都是D的父类,C是D的直接父类,AB是D ...
- Linux简介及常用命令使用2--linux常用命令:查看 删除 编辑 创建等
cd . // 进入当前目录 cd ~ //进入根目录 pwd //当前路径 echo "my name is makaidong">makai ...
- Getaddrinfo()笔记
WSADATA dwRetval; if (WSAStartup(MAKEWORD(2,2),&dwRetval)!=0) //开启Socket { printf("WSAStart ...
- actionlib的身世之谜
不知道为什么会把这么严肃认真的一篇技术整理贴起这么一个故事会风格类似的名字,就这样吧:^)shenmegui 园子里有人整理了actionlib的初学者教程,我来整理下actionlib的细节描述吧. ...
- label与input间距的小问题
先码后文 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- mysql中文乱码问题总结
起因:此次开发工作由于电脑不能正常使用sqlserver所以改用mysql. 问题描述:mysql使用中文产生乱码 详细问题: 1.问题1:在dos界面登录数据库存入数据时如果存在中文那么不会显示. ...