定义:(Chain of Responsibility)
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
类图:
责任链模式通用类图启示:
用过ERP软件的都知道,从生产制造到采购销售,库存管理到财务管理,各个模块下各个环节基本上都是按照既定业务流程进行规范作业的。各个环节都有对应的人员负责,承担相应职责。
拿采购环节举例:
开发经理向公司反馈需要要给程序猿升级设备以提高工作效率,申请采购一批电脑设备。公司采购部门有采购员小责做了一张采购申请单,保存后,需要直接上级采购经理任经理确认提交审核,若总金额超过经理审核最大额度,需继续提交到公司老总审批,只有CEO链总批准后,这张采购申请单才能生效。
这个环节中共包含三个主要操作,保存、提交、审核,且分别对应三个角色进行处理。
在这个例子中,采购员负责做单;采购经理负责检查确认;CEO负责审核。
采购员具有权限:保存
采购经理具有权限:保存、提交、审核(只可审核总金额低于5万的订单)
CEO具有权限:保存、提交、审核
这不就是一条业务被不同职责分割成三个操作吗?
来,咱们用『责任链模式』一探究竟。
代码:
先来定义单据,单据主要包括单据名称、单据编号、物料、数量、价格、总金额,还有制单员
/// <summary>
/// 单据状态枚举
/// </summary>
public enum BillStatus
{
Open,
Saved,
Submitted,
Audited
}
public abstract class Bill
{
public string BillName { get; set; }
public string BilNo { get; set; }
public BillStatus Status { get; set; }
public string MaterialName { get; set; }
public int Qty { get; set; }
public decimal Price { get; set; }
public decimal Amount
{
get
{
return Qty * Price;
}
}
/// <summary>
/// 做单员
/// </summary>
public BillHandler BillMaker { get; set; }
}
public class PurchaseBill : Bill
{
public PurchaseBill()
{
base.Status = BillStatus.Open;
base.BillName = "采购申请单";
}
}
细心的同学注意到我们单据中定义的制单员BillMaker
引入了BillHandler
,这个就是我们『责任链模式』的核心所在,Handler角色。主要定义并实现了一个HandleBill方法(模板方法模式)供子类共用;同时定义了一个抽象方法DoBillOperation让子类实现自己独立的业务逻辑;还有一个重要的属性Successor
,即下一处理者,是流程流转的必要属性啊,切记切记。
另外:单据的审批流是基于角色、权限的,所以我在BillHandler类定义了Permissions权限集合,和公共的权限检查方法。
上代码:
/// <summary>
/// 单据处理角色
/// </summary>
public abstract class BillHandler
{
/// <summary>
/// 单据处理者姓名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 单据处理者具有的权限
/// </summary>
public List<string> Permissions { get; set; }
public bool CheckPermission(string permission)
{
return Permissions.Contains(permission);
}
//声明下一个处理者
protected BillHandler Successor { get; set; }
/// <summary>
/// 处理单据
/// </summary>
/// <param name="bill"></param>
public void HandleBill(Bill bill)
{
//单据处理从保存开始
if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
{
this.DoBillOperation(bill);
}
else
{
this.Successor.DoBillOperation(bill);
}
}
public abstract void DoBillOperation(Bill bill);
}
先来看看采购员的实现:权限是动态的,为了演示方便,我们在代码中写死权限。虽然采购员只有保存权限,但是我们还是给他实现了提交、审核的逻辑。因为权限是动态的嘛。(代码中限定只可审核金额低于5000元的单);且在默认构造函数指定下一流程处理者(经理)
public class Purchaser : BillHandler
{
private List<string> permissions = new List<string>() { "SAVE" };
public Purchaser(string username)
{
base.UserName = username;
base.Permissions = permissions;
base.Successor = new Manager("经理--任经理");//在构造函数中指定下一个处理者
}
public override void DoBillOperation(Bill bill)
{
if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
{
bill.Status = BillStatus.Saved;
Console.WriteLine(string.Format("{0}:{1}已经保存!", this.UserName, bill.BilNo));
}
if (CheckPermission("SUBMIT") && bill.Status == BillStatus.Saved)
{
bill.Status = BillStatus.Submitted;
Console.WriteLine(string.Format("{0}:{1}已经提交!", this.UserName, bill.BilNo));
}
if (CheckPermission("AUDIT") && bill.Status == BillStatus.Submitted)
{
if (bill.Amount <= 5000)
{
bill.Status = BillStatus.Submitted;
Console.WriteLine(string.Format("{0}:{1}已经审核!", this.UserName, bill.BilNo));
}
else
{
this.Successor.DoBillOperation(bill);
}
}
else
{
this.Successor.DoBillOperation(bill);
}
}
}
下面来看看经理,在默认构造函数指定下一流程处理者(CEO),同时在业务逻辑中限定只可审核金额低于50000元的单。
public class Manager : BillHandler
{
private List<string> permissions = new List<string>() { "SAVE", "SUBMIT", "AUDIT" };
public Manager(string userName)
{
base.UserName = userName;
base.Permissions = permissions;
base.Successor = new CEO("CEO--链总");
}
public override void DoBillOperation(Bill bill)
{
if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
{
bill.Status = BillStatus.Saved;
Console.WriteLine(string.Format("{0}:{1}已经保存!", this.UserName, bill.BilNo));
}
if (CheckPermission("SUBMIT") && bill.Status == BillStatus.Saved)
{
bill.Status = BillStatus.Submitted;
Console.WriteLine(string.Format("{0}:{1}已经提交!", this.UserName, bill.BilNo));
}
if (CheckPermission("AUDIT") && bill.Status == BillStatus.Submitted)
{
if (bill.Amount <= 20000)
{
bill.Status = BillStatus.Submitted;
Console.WriteLine(string.Format("{0}:{1}已经审核!", this.UserName, bill.BilNo));
}
else
{
this.Successor.DoBillOperation(bill);
}
}
else
{
this.Successor.DoBillOperation(bill);
}
}
}
最后上公司CEO,,注意流程结束了,就不需要指定下一流程处理者了:
public class CEO : BillHandler
{
private List<string> permissions = new List<string>() { "SAVE", "SUBMIT", "AUDIT" };
public CEO(string userName)
{
base.UserName = userName;
base.Permissions = permissions;
}
public override void DoBillOperation(Bill bill)
{
if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
{
bill.Status = BillStatus.Saved;
Console.WriteLine(string.Format("{0}:{1}已经保存!", this.UserName, bill.BilNo));
}
if (CheckPermission("SUBMIT") && bill.Status == BillStatus.Saved)
{
bill.Status = BillStatus.Submitted;
Console.WriteLine(string.Format("{0}:{1}已经提交!", this.UserName, bill.BilNo));
}
if (CheckPermission("AUDIT") && bill.Status == BillStatus.Submitted)
{
bill.Status = BillStatus.Submitted;
Console.WriteLine(string.Format("{0}:{1}已经审核!", this.UserName, bill.BilNo));
}
}
}
看看测试代码及结果
static void Main(string[] args)
{
PurchaseBill bill = new PurchaseBill()
{
BilNo = "CGDD001",
MaterialName = "惠普电脑",
Qty = 50,
Price = 5000,
BillMaker = new Purchaser("采购员--小责")
//BillMaker = new Manager("经理--任经理")
//BillMaker = new CEO("CEO--链总")
};
Console.WriteLine(string.Format("创建采购申请单:{0};申请购买{1}台{2}", bill.BilNo, bill.Qty, bill.MaterialName));
bill.BillMaker.HandleBill(bill);
Console.ReadLine();
}
运行结果
从中我们可以看到,我们是通过制单员去调用单据处理方法,同时我们可以灵活替换掉制单员,因为实际业务场景也不是限定只有采购员才可以做单的嘛。感兴趣的可以更改数量,看看不同的金额下,最终走了哪些流程。
优缺点:
优点:
责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。
缺点:
责任链有两个非常显著的缺点:一是性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。二是调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
应用场景:
流程业务,各司其职,就可以考虑『责任链模式』
总结:
『责任链模式』其实是『模板方法模式』的拓展,责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果。
网友评论