什么是責(zé)任鏈?
責(zé)任鏈模式是行為模式的一種,它將需要觸發(fā)的Handler組成一條鏈,發(fā)送者將請(qǐng)求發(fā)給鏈的第一個(gè)接收者,并且沿著這條鏈傳遞,直到有一個(gè)Handler來處理它或者直到最后也沒有對(duì)象處理而留在鏈末尾端;責(zé)任連模式的重點(diǎn)是在鏈上,由一條鏈去處理相似的請(qǐng)求,在鏈中決定誰來處理這個(gè)請(qǐng)求
責(zé)任鏈分為純責(zé)任鏈與不純責(zé)任鏈(一般實(shí)戰(zhàn)應(yīng)用較多)
**純職責(zé)鏈 **
1.每個(gè)處理者接收到請(qǐng)求后,要么單純轉(zhuǎn)發(fā)請(qǐng)求,要么單純處理請(qǐng)求。不允許既處理請(qǐng)求,又轉(zhuǎn)發(fā)請(qǐng)求的情況。
2.請(qǐng)求必須被責(zé)任鏈上的某個(gè)處理者處理。不允許出現(xiàn)請(qǐng)求未被處理的情況。
**不純職責(zé)鏈 **
1.每個(gè)處理者接收到請(qǐng)求后,除了單純轉(zhuǎn)發(fā)請(qǐng)求,或者單純處理請(qǐng)求,還可以部分處理請(qǐng)求后,轉(zhuǎn)發(fā)。
2.請(qǐng)求可以不被責(zé)任鏈上的任何處理者處理。
責(zé)任鏈優(yōu)點(diǎn)
1.解耦請(qǐng)求者和處理者。
2.非常靈活,可以任意組裝
3.各個(gè)節(jié)點(diǎn)的責(zé)任明確
缺點(diǎn)
1.每次都是從鏈頭開始。
2. 可能造成死循環(huán)。責(zé)任鏈如果是環(huán)狀的,可能會(huì)導(dǎo)致循環(huán)調(diào)用,造成死循環(huán)。
純責(zé)任鏈的代碼Demo:
/**
* 在公司OA系統(tǒng)請(qǐng)假審批流程
如果請(qǐng)假小于3天只需要項(xiàng)目經(jīng)理批復(fù)就行;
如果請(qǐng)假大于等于3天,小于7天需要人事經(jīng)理批復(fù)了;
如果請(qǐng)假大于等于7天,小于15天需要總經(jīng)理批復(fù)了;
如果申請(qǐng)請(qǐng)假大于等于15天,決絕批復(fù)......
* @param args
*/
public static void main(String[] args) {
Leader manager = new Manager("張三");
Leader hrManager = new HrManager("李四");
Leader generalManager = new GeneralManager("王麻子");
//組織責(zé)任鏈對(duì)象的關(guān)系
manager.setNextLeader(hrManager);
hrManager.setNextLeader(generalManager);
//請(qǐng)假
LeaveRequest request = new LeaveRequest("parry", 10, "回家相親!");
manager.dealLeaveRequest(request);
}
public abstract class Leader {
// 領(lǐng)導(dǎo)姓名
protected String name;
// 責(zé)任鏈上的后繼對(duì)象
protected Leader nextLeader;
public Leader(String name) {
super();
this.name = name;
}
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
public abstract void dealLeaveRequest(LeaveRequest request);
}
public class HrManager extends Leader{
public HrManager(String name) {
super(name);
}
@Override
public void dealLeaveRequest(LeaveRequest request) {
if (3 <= request.getLeaveDay() && request.getLeaveDay() < 7) {
System.out.println("人事經(jīng)理:" + name + " 審批了 " + request.getEmployee() + "請(qǐng)假" + request.getLeaveDay()
+ "天的請(qǐng)求,請(qǐng)假原因:" + request.getReason());
} else {
if (this.nextLeader != null) {
this.nextLeader.dealLeaveRequest(request);
}
}
}
}
public class GeneralManager extends Leader {
public GeneralManager(String name) {
super(name);
}
@Override
public void dealLeaveRequest(LeaveRequest request) {
if (7 <= request.getLeaveDay() && request.getLeaveDay() <= 15) {
System.out.println("總經(jīng)理:" + name + " 審批了 " + request.getEmployee() + "請(qǐng)假" + request.getLeaveDay()
+ "天的請(qǐng)求,請(qǐng)假原因:" + request.getReason());
} else {
System.out.println(
"總經(jīng)理:" + name + " 拒絕了 " + request.getEmployee() + "請(qǐng)假" + request.getLeaveDay() + "天的請(qǐng)求,請(qǐng)假不能超過15天");
}
}
}
public class Manager extends Leader{
public Manager(String name) {
super(name);
}
@Override
public void dealLeaveRequest(LeaveRequest request) {
if (request.getLeaveDay() < 3) {
System.out.println("經(jīng)理:" + name + " 審批了 " + request.getEmployee() + "請(qǐng)假" + request.getLeaveDay()
+ "天的請(qǐng)求,請(qǐng)假原因:" + request.getReason());
} else {
if (this.nextLeader != null) {
this.nextLeader.dealLeaveRequest(request);
}
}
}
}
@Data
public class LeaveRequest {
//姓名
private String name;
//請(qǐng)假天數(shù)
private int leaveDay;
//請(qǐng)假原因
private String reason;
public LeaveRequest(String name, int leaveDay, String reason) {
this.name=name;
this.leaveDay=leaveDay;
this.reason=reason;
}
}
電商售后使用責(zé)任鏈(不純責(zé)任鏈)
售后業(yè)務(wù)梳理
電商售后常見三種模式:
僅退款、退款退貨、退款換貨
售后業(yè)務(wù)可分為
僅退款: 采購(gòu)商申請(qǐng)——>商家審核——>完成
退款退貨:采購(gòu)商申請(qǐng)——>商家審核——>采購(gòu)商發(fā)貨——>商家確認(rèn)收貨——>完成
退款換貨:采購(gòu)商申請(qǐng)——>商家審核——>采購(gòu)商發(fā)貨——>商家確認(rèn)收貨——>商家發(fā)出新品——>采購(gòu)商確認(rèn)收貨——>完成
僅退款的實(shí)際退款等操作在商家審核時(shí),而退款退貨和退款換貨則在確認(rèn)收貨時(shí)。
以下為售后責(zé)任鏈流程:
售后->校驗(yàn)退款單狀態(tài)->原路退款(以下單支付方式為準(zhǔn))->退積分或其它優(yōu)惠->更新訂單及對(duì)應(yīng)商品退款信息->保存結(jié)算->保存財(cái)務(wù)流水->保存/更新退款日志->發(fā)送退款成功異步消息
三種售后的核心退款業(yè)務(wù)流程基本是一致的,少個(gè)別節(jié)點(diǎn)業(yè)務(wù)邏輯有所區(qū)別,將核心業(yè)務(wù)抽成責(zé)任鏈的各個(gè)節(jié)點(diǎn),這對(duì)售后來說,代碼的復(fù)用性提高了很多,同時(shí)業(yè)務(wù)處理更加清晰,犯錯(cuò)率大大降低。
以下為售后中代碼使用Demo
定義一個(gè)節(jié)點(diǎn)的基類接口
public interface BaseRefundHandler {
void handle(PipelineContext context) ;
}
售后責(zé)任鏈組裝,以下以僅退款為例
public class RefundPipeBean {
/**
* 僅退款,責(zé)任鏈初始化節(jié)點(diǎn)
*/
public List<String> refundPipe = new ArrayList<>();
public List<String> getRefundPipe() {
refundPipe.add("checkRefundStatus"); //校驗(yàn)退款單狀態(tài)
refundPipe.add("refundMoney"); //原路退款(獲取訂單支付方式)
refundPipe.add("returnScore"); //退積分或其它優(yōu)惠券類
......
return refundPipe;
}
@Bean
@Scope("prototype")
public CheckRefundStatus checkRefundStatus() {return new CheckRefundStatus();}
@Bean
@Scope("prototype")
public RefundMoney refundMoney() {return new RefundMoney();}
......
}
** 注: 這個(gè)配置就等同于之前在xml里的配置**
"checkRefundStatus" class="com.xx.CheckRefundStatus"/>
創(chuàng)建一個(gè)啟動(dòng)監(jiān)聽器
@Component
public class RefundPipeListener extends ApplicationObjectSupport implements InitializingBean {
private static Map<String, List > handlersMap = new HashMap<>();
public static Map<String, List> getHandlersMap() {
return handlersMap;
}
//最好定義成全局的,此處因演示 才在類中寫 。 責(zé)任鏈的名稱
public static String ONLY_REFUND="ONLY_REFUND";
@Override
public void afterPropertiesSet() throws Exception {
RefundPipeBean refundPipeBean=new RefundPipeBean();
//裝載退款業(yè)務(wù)鏈.....
List normalHandlers=new ArrayList<>();
for (String s : refundPipeBean.getRefundPipe()) {
BaseRefundHandler normalHandler = (BaseRefundHandler) getApplicationContext().getBean(s);
normalHandlers.add(normalHandler);
handlersMap.put(ONLY_REFUND, normalHandlers);
}
}
}
創(chuàng)建節(jié)點(diǎn)傳遞對(duì)象所用POJO類
@Data
public class PipelineContext<K, V> extends ConcurrentHashMap<K, V> {
protected boolean success = false;
//責(zé)任鏈結(jié)束標(biāo)識(shí)
protected boolean isEnd = false;
protected String resultCode;
private Object data;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public boolean isEnd() {
return isEnd;
}
public void setEnd(boolean end) {
isEnd = end;
}
}
public class RefundPipelineContext<K, V> extends PipelineContext<K, V> {
/**
* 封裝退款單信息
*/
private RefundInfoPipeVo refundInfoPipeVo;
public RefundInfoPipeVo getRefundInfoPipeVo() {
return refundInfoPipeVo;
}
public void setRefundInfoPipeVo(RefundInfoPipeVo refundInfoPipeVo) {
this.refundInfoPipeVo = refundInfoPipeVo;
}
}
創(chuàng)建節(jié)點(diǎn)
public class CheckRefundStatus implements BaseRefundHandler {
@Override
public void handle(PipelineContext<String, RefundInfoPipeVo> context) {
//業(yè)務(wù)邏輯 todo
......
//表示該節(jié)點(diǎn)運(yùn)行正常,可以繼續(xù)向下走
context.setSuccess(true);
}
}
業(yè)務(wù)調(diào)用層
@RestController
public class RefundInfoController {
@Autowired
RefundInfoService refundInfoService;
@ApiOperation("僅退款")
@RequestMapping(value = "/auditRefund", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json;charset=UTF-8")
public RespData<Boolean> auditRefund(xxx) {
refundInfoService.auditRefund(xxx);
return RespData.success("審核成功",true);
}
}
public interface RefundInfoService {
void auditRefund(xxx);
}
@Service("refundInfoService")
public class RefundInfoServiceImpl implements RefundInfoService {
@Override
public void auditRefund(xxx) {
//業(yè)務(wù)邏輯 todo
RefundInfoResultVo refundInfoResultVo = refundInfoMapper.queryById(platformId, refundId);
......
PipelineContext<String, RefundInfoPipeVo> refund = new RefundPipelineContext<>();
//在責(zé)任鏈中傳遞參數(shù)對(duì)象
refund.put("refund", refundInfoResultVo);
Map<String, List> map = RefundPipelineChangeListener.getHandlersMap();
//通過責(zé)任鏈的名稱獲取對(duì)應(yīng)鏈 。 此處key值應(yīng)為一個(gè)全局的常量,與上面監(jiān)聽器中的鏈條名一致
List baseRefundHandlers = map .get(ONLY_REFUND);
for (BaseRefundHandler handler : baseRefundHandlers) {
handler.handle(refund);
if (refund.isEnd()) {
System.out.println("責(zé)任鏈執(zhí)行結(jié)束...");
break;
}
if (!refund.isSuccess()) {
System.out.println("責(zé)任鏈執(zhí)行失敗...");
break;
}
}
}
}
整體總結(jié)
以上是責(zé)任鏈在業(yè)務(wù)中創(chuàng)建及使用的流程。
同時(shí)以上流程也還存在一些可優(yōu)化點(diǎn):
- 責(zé)任鏈節(jié)點(diǎn)硬編碼問題
- 添加同步事務(wù)控制
-
模式
+關(guān)注
關(guān)注
0文章
65瀏覽量
13401 -
handler
+關(guān)注
關(guān)注
0文章
7瀏覽量
3035
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論