Definition
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
Participants
The classes and objects participating in this pattern are:
Handler (Approver)
- defines an interface for handling the requests
- (optional) implements the successor link
ConcreteHandler (Director, VicePresident, President)
- handles requests it is responsible for
- can access its successor
- if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor
Client (ChainApp)
- initiates the request to a ConcreteHandler object on the chain
责任链模式代码结构
//-------------------------------------------------------------------------------------
// ChainOfResponsibilityStructure.cs
//-------------------------------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class ChainOfResponsibilityStructure : MonoBehaviour
{
void Start()
{
// Setup Chain of Responsibility
Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
Handler h3 = new ConcreteHandler3();
h1.SetSuccessor(h2);
h2.SetSuccessor(h3);
// Generate and process request
int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };
foreach (int request in requests)
{
h1.HandleRequest(request);
}
}
}
/// <summary>
/// The 'Handler' abstract class
/// </summary>
abstract class Handler
{
protected Handler successor;
public void SetSuccessor(Handler successor)
{
this.successor = successor;
}
public abstract void HandleRequest(int request);
}
/// <summary>
/// The 'ConcreteHandler1' class
/// </summary>
class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Debug.Log(this.GetType().Name + " handled request " + request);
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
/// <summary>
/// The 'ConcreteHandler2' class
/// </summary>
class ConcreteHandler2 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 10 && request < 20)
{
Debug.Log(this.GetType().Name + " handled request " + request);
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
/// <summary>
/// The 'ConcreteHandler3' class
/// </summary>
class ConcreteHandler3 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 20 && request < 30)
{
Debug.Log(this.GetType().Name+" handled request "+request);
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
案例1:公司账单报销
//-------------------------------------------------------------------------------------
// ChainOfResponsibilityExample1.cs
//-------------------------------------------------------------------------------------
//This real-world code demonstrates the Chain of Responsibility pattern in which several linked
//managers and executives can respond to a purchase request or hand it off to a superior.
//Each position has can have its own set of rules which orders they can approve.
//这个代码演示了几个链接的责任链模式的实际应用
//经理和主管可以回应购买请求或将其交给上级。
//每个职位都可以有自己的一套规则,他们可以批准这些规则。
using UnityEngine;
namespace ChainOfResponsibilityExample1
{
public class ChainOfResponsibilityExample1 : MonoBehaviour
{
void Start ( )
{
// Setup Chain of Responsibility
Approver larry = new Director(); //经理
Approver sam = new VicePresident(); //副总
Approver tammy = new President(); //总裁
larry.SetSuccessor(sam);
sam.SetSuccessor(tammy);
// Generate and process purchase requests
Purchase p = new Purchase(2034, 350.00, "¥350车费报销");
larry.ProcessRequest(p);
p = new Purchase(2035, 32590.10, "¥32590.10 设备采购");
larry.ProcessRequest(p);
p = new Purchase(2036, 122100.00, "¥122100 投资建厂");
larry.ProcessRequest(p);
}
}
/// <summary>
/// The 'Handler' abstract class
/// </summary>
abstract class Approver
{
protected Approver successor;
public void SetSuccessor(Approver successor)
{
this.successor = successor;
}
public abstract void ProcessRequest(Purchase purchase);
}
/// <summary>
/// The 'ConcreteHandler' class
/// </summary>
class Director : Approver
{
public override void ProcessRequest(Purchase purchase)
{
if (purchase.Amount < 10000.0)
{
Debug.Log(this.GetType().Name+" approved request# "+purchase.Number+purchase.Purpose);
}
else if (successor != null)
{
successor.ProcessRequest(purchase);
}
}
}
/// <summary>
/// The 'ConcreteHandler' class
/// </summary>
class VicePresident : Approver
{
public override void ProcessRequest(Purchase purchase)
{
if (purchase.Amount < 25000.0)
{
Debug.Log(this.GetType().Name + " approved request# " + purchase.Number + purchase.Purpose);
}
else if (successor != null)
{
successor.ProcessRequest(purchase);
}
}
}
/// <summary>
/// The 'ConcreteHandler' class
/// </summary>
class President : Approver
{
public override void ProcessRequest(Purchase purchase)
{
if (purchase.Amount < 100000.0)
{
Debug.Log(this.GetType().Name + " approved request# " + purchase.Number + purchase.Purpose);
}
else
{
Debug.Log("Request# "+purchase.Number+ "requires an executive meeting!" + purchase.Number + purchase.Purpose);
}
}
}
/// <summary>
/// Class holding request details
/// </summary>
class Purchase
{
private int _number;
private double _amount;
private string _purpose;
// Constructor
public Purchase(int number, double amount, string purpose)
{
this._number = number;
this._amount = amount;
this._purpose = purpose;
}
// Gets or sets purchase number
public int Number
{
get { return _number; }
set { _number = value; }
}
// Gets or sets purchase amount
public double Amount
{
get { return _amount; }
set { _amount = value; }
}
// Gets or sets purchase purpose
public string Purpose
{
get { return _purpose; }
set { _purpose = value; }
}
}
}
案例2:加减乘除
//-------------------------------------------------------------------------------------
// ChainOfResponsibilityExample2.cs
//-------------------------------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class ChainOfResponsibilityExample2 : MonoBehaviour
{
void Start ( )
{
// create calculation objects that get chained to each other in a sec
Chain calc1 = new AddNumbers();
Chain calc2 = new SubstractNumbers();
Chain calc3 = new DivideNumbers();
Chain calc4 = new MultiplyNumbers();
// now chain them to each other
calc1.SetNextChain(calc2);
calc2.SetNextChain(calc3);
calc3.SetNextChain(calc4);
// this is the request that will be passed to a chain object to let them figure out which calculation objects it the right for the request
// the request is here the CalculationType enum we add. so we want this pair of numbers to be added
Numbers myNumbers = new Numbers(3, 5, CalculationType.Add);
calc1.Calculate(myNumbers);
// another example:
Numbers myOtherNumbers = new Numbers(6, 2, CalculationType.Multiply);
calc1.Calculate(myOtherNumbers);
// or pass it to some chain object inbetween which will not work in this case:
Numbers myLastNumbers = new Numbers(12, 3, CalculationType.Substract);
calc3.Calculate(myLastNumbers);
}
// just defining some types of calculation we want to implement
// it is better than passing string values as requests because you don't risk any typos that way :)
public enum CalculationType
{
Add,
Substract,
Divide,
Multiply
};
// We use this object as an example object to be passed to the calculation chain ;-)
// to figure out what we want to do with it (which is stored in CalculationType/calculationWanted)
public class Numbers
{
// some numbers:
public int number1 { get; protected set; }
public int number2 { get; protected set; }
// here we store in this object what we want to do with it to let the chain figure out who is responsible for it ;-)
public CalculationType calculationWanted { get; protected set; }
// constructor:
public Numbers(int num1, int num2, CalculationType calcWanted)
{
this.number1 = num1;
this.number2 = num2;
this.calculationWanted = calcWanted;
}
}
// doesn't need to be called chain of course ;-)
public interface Chain
{
void SetNextChain(Chain nextChain); // to be called when calulcation fails
void Calculate(Numbers numbers); // try to calculate
}
public class AddNumbers : Chain
{
// each chain object stored a private nextInChain object, that gets called when the method calculate fails
protected Chain nextInChain;
public void SetNextChain(Chain nextChain)
{
this.nextInChain = nextChain;
}
public void Calculate(Numbers request)
{
if(request.calculationWanted == CalculationType.Add)
{
Debug.Log("Adding: " + request.number1 + " + " + request.number2 + " = " + (request.number1 + request.number2).ToString());
}
else if(nextInChain != null)
nextInChain.Calculate(request);
else
Debug.Log ("Handling of request failed: " + request.calculationWanted);
}
}
public class SubstractNumbers : Chain
{
protected Chain nextInChain;
public void SetNextChain(Chain nextChain)
{
this.nextInChain = nextChain;
}
public void Calculate(Numbers request)
{
if(request.calculationWanted == CalculationType.Substract)
{
Debug.Log("Substracting: " + request.number1 + " - " + request.number2 + " = " + (request.number1 - request.number2).ToString());
}
else if(nextInChain != null)
nextInChain.Calculate(request);
else
Debug.Log ("Handling of request failed: " + request.calculationWanted);
}
}
public class DivideNumbers : Chain
{
protected Chain nextInChain;
public void SetNextChain(Chain nextChain)
{
this.nextInChain = nextChain;
}
public void Calculate(Numbers request)
{
if(request.calculationWanted == CalculationType.Divide)
{
Debug.Log("Dividing: " + request.number1 + " / " + request.number2 + " = " + (request.number1 / request.number2).ToString());
}
else if(nextInChain != null)
nextInChain.Calculate(request);
else
Debug.Log ("Handling of request failed: " + request.calculationWanted);
}
}
public class MultiplyNumbers : Chain
{
protected Chain nextInChain;
public void SetNextChain(Chain nextChain)
{
this.nextInChain = nextChain;
}
public void Calculate(Numbers request)
{
if(request.calculationWanted == CalculationType.Multiply)
{
Debug.Log("Multiplying: " + request.number1 + " * " + request.number2 + " = " + (request.number1 * request.number2).ToString());
}
else if(nextInChain != null)
nextInChain.Calculate(request);
else
Debug.Log ("Handling of request failed: " + request.calculationWanted);
}
}
}