耽误了很久,重新开始写一些设计模式,打算用Unity来实现,同时找到了一个开源库,授人以鱼不如授人以渔。
https://lab.uwa4d.com/lab/5b442b98d7f10a201faf6c69
这个模式简单的来说就是像链表一样。每个节点定义自己的处理函数,处理不了传给下个节点。
官方的解释是:
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
何时使用:在处理消息的时候以过滤很多道。
如何解决:拦截的类都实现统一接口。
关键代码:Handler 里面聚合它自己,在 HanleRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
应用实例: 1、红楼梦中的"击鼓传花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
注意事项:在 JAVA WEB 中遇到很多应用。
实例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;
using System.Collections;
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, "Assets");
larry.ProcessRequest(p);
p = new Purchase(2035, 32590.10, "Project X");
larry.ProcessRequest(p);
p = new Purchase(2036, 122100.00, "Project Y");
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);
}
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);
}
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);
}
else
{
Debug.Log("Request# "+purchase.Number+ "requires an executive meeting!");
}
}
}
/// <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);
}
}
}
自己总结了下差不多是if else switch等的加强版,特别适合当有复杂的或者同时进行多个判断需要进行不同的复杂的处理。
而且还有一个优点就是,这个相当于一个整体的对象,可以同时被多个其他需要的对象调用。