第二十四章 职责链模式(ChainofResposibility)
意图
是对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直道一个对象处理它为止。
使用场合
1.有多个对象可以处理一个请求,那个对象处理该请求在运行的时候自动确定。
2.希望在不明确制定接收者的情况下,向多个对象中的一个提交一个请求。
......
意图
是对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直道一个对象处理它为止。
使用场合
1.有多个对象可以处理一个请求,那个对象处理该请求在运行的时候自动确定。
2.希望在不明确制定接收者的情况下,向多个对象中的一个提交一个请求。
3.可处理一个请求的对象集合应被动态的指定。
例如:
1.过滤器:处理或拦截信息,不同的过滤器可以过滤不同的类型的信息,过滤器可以组成职责链模式。
2.时间处理器:不同事件可以有不同的事件处理器,时间处理器组成职责链。然后用户可以不关心事件的类型,交给职责链处理即可。
3.异常处理器:不同的异常处理器对应不同的异常。
4.文法分析器:对给定的Tekon进行文法分析,通过扩充职责链扩充文法分析器的功能。
5.分段计算:职责链上的对象在符合条件时进行计算,这样可以去掉switch语句。
结构
1.Handler:定一个处理请求的接口并实现后续链。
2.ConcreteHandler:处理负责的请求并可访问后继者。如果可以处理该请求,则处理之;否则将该请求转发给后继者。
3.向链上的具体处理对象提交请求。
职责链模式构成与执行效率
有些情况下,职责链中各对象完成的任务没有包含性,如在文法分析其中各种Tekon是平行关系。这时,职责链中对象的顺序对执行结果没有影响。然而由于职责链是串行的德,链中的前一个对象无法完成任务i就交给后一个对象完成。因此职责链中对象的顺序组织方式不同,会大大影响程序的执行效率。在组织职责链对象的顺序时,应将较常用的对象放置在链的前端。
结果
使用职责链降低了请求与响应的耦合性,职责链的顺序可以有用户来决定。采用职责链的不足如果没有适合处理请求的对象,请求将对不到处理。
文法分析器——四则运算式解析这里我们的目的是将普通中则表达式转化为后则表达式。
方案:四则运算式涉及到4种Tekon,即参与运算的数字、运算符(+-*/以及将来肯能扩充的运算符)、左括号 ( 和右括号 ) 。
为了方便字符串分割,这里将字符串的格式规定为数值和运算符、运算符与运算符之间都用空格隔开。 例如"(_2_+_3_)_/_3"
采用ArrayList模拟堆栈
Stack.cs
using System;
using System.Collections;
using System.Text;
namespace ChainofResposibility.EX24_3
{
/**//// <summary>
/// Stact采用ArrayList模拟堆栈
/// </summary>
class AStack
{
private ArrayList myStact;
public int Count
{
get { return myStact.Count; }
}
public AStack()
{
myStact=new ArrayList();
}
public void Push(Object o)
{
myStact.Add(o);
}
public Object Pop()
{
int idx = myStact.Count - 1;
if (idx < 0)
{
throw new Exception("堆栈已空");
}
object o = myStact[idx];
myStact.RemoveAt(idx);
return o;
}
public Object Pop2()
{
if (myStact.Count == 0)
{
throw new Exception("堆栈已空");
}
object o = myStact[0];
myStact.RemoveAt(0);
return o;
}
public bool IsEmpty()
{
return(myStact.Count==0);
}
public object TopItem()
{
int idx = myStact.Count - 1;
if (idx<0)
{
throw new Exception("堆栈已空");
}
object o = myStact[idx];
return o;
}
}
}
处理四则表达式中token的基础类
TreatToken.cs
using System;
using System.Collections;
using System.Text;
namespace ChainofResposibility.EX24_3
{
/**//// <summary>
/// TreatToken处理四则表达式中token的基础类
/// </summary>
abstract class TreatToken
{
//用来处理token的临时堆栈
protected AStack st;
//用来保存结果的堆栈
protected AStack stOutput;
//Next构成职责链
protected TreatToken _next;
public TreatToken Next
{
get { return _next; }
set { _next = value; }
}
public TreatToken(AStack st, AStack stout)
{
this.st = st;
this.stOutput=stout;
}
public abstract void Treat(string s);
}
}
在这个抽象类中定义了职责链的关系、处理过程中需要用到的堆栈和处理接口。本例有两个Tekon需要处理,即数字和运算符,因为分别对应处理这两种类型的子类:
NumberToken.cs
using System;
using System.Collections;
using System.Text;
using CommonFrameworkWBs.ValidateTool;
namespace ChainofResposibility.EX24_3
{
/**//// <summary>
/// NumberToken处理数字
/// </summary>
class NumberToken:TreatToken
{
public NumberToken(AStack st, AStack stout)
: base(st, stout)
{
}
public override void Treat(string s)
{
ValidateNumber vn = new ValidateNumber();
if (vn.IsNumber(s))
{
this.stOutput.Push(s);
}
else
{
this.Next.Treat(s);
}
}
}
}
处理数字的部分很简单,只要将数字如栈即可。应为后则表达式通过运算符的顺序控制计算,所以参与运算的数字顺序不变。
处理运算符的代码:
OperatorToken.cs
using System;
using System.Collections;
using System.Text;
namespace ChainofResposibility.EX24_3
{
/**//// <summary>
/// OperatorToken处理操作符+-*/()等
/// </summary>
class OperatorToken:TreatToken
{
private MyOperatoer ope;
public OperatorToken(AStack st, AStack stout, MyOperatoer op)
: base(st, stout)
{
this.ope = op;
}
public override void Treat(string s)
{
if (s == ope.OP)
{
if (st.IsEmpty() || s == "(")
{
st.Push(ope);
}
else
{
MyOperatoer o = (MyOperatoer)st.TopItem();
while (o.Level >= ope.Level || ope.OP == ")")
{
MyOperatoer o1 = (MyOperatoer)st.Pop();
if (o1.OP == "(")
{
break;
}
this.stOutput.Push(o1);
if (st.IsEmpty())
{
break;
}
o=(MyOperatoer)st.TopItem();
}
if (ope.OP != ")")
{
st.Push(ope);
}
}
}
else
{
this.Next.Treat(s);
}
}
}
}
处理未知的运算符的类:
OtherToken.cs
using System;
using System.Collections;
using System.Text;
namespace ChainofResposibility.EX24_3
{
class OtherToken:TreatToken
{
public OtherToken()
: base(null, null)
{
}
public override void Treat(string s)
{
if (s != "")
{
throw new Exception("无法识别的操作符"+s);
}
}
}
}
负责将组装职责链并完成计算:
Parser.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace ChainofResposibility.EX24_3
{
/**//// <summary>
/// Parser采用职责链将四则元算转化为后表达式
/// </summary>
class Parser
{
AStack sttemp = new AStack();
AStack stoutput = new AStack();
TreatToken tk;
public Parser()
{
NumberToken nt=new NumberToken(sttemp,stoutput);
OperatorToken op1 = new OperatorToken(sttemp,stoutput,new MyOperatoer("+",11));
OperatorToken op2 = new OperatorToken(sttemp, stoutput, new MyOperatoer("-", 11));
OperatorToken op3 = new OperatorToken(sttemp, stoutput, new MyOperatoer("*", 10));
OperatorToken op4 = new OperatorToken(sttemp, stoutput, new MyOperatoer("/", 10));
OperatorToken op5 = new OperatorToken(sttemp, stoutput, new MyOperatoer("%", 10));
OperatorToken op6 = new OperatorToken(sttemp, stoutput, new MyOperatoer("(", 9));
OperatorToken op7 = new OperatorToken(sttemp, stoutput, new MyOperatoer(")", 8));
OtherToken ot = new OtherToken();
nt.Next = op1;
op1.Next = op2;
op2.Next = op3;
op3.Next = op4;
op4.Next = op5;
op5.Next = op6;
op6.Next = op7;
op7.Next=ot;
tk = nt;
}
public string Do(string s)
{
string sout = "";
string[] strArr = GetStrList(s);
for (int i = 0; i < strArr.Length; i++)
{
tk.Treat(strArr[i]);
}
while (!sttemp.IsEmpty())
{
stoutput.Push(sttemp.Pop());
}
while (!stoutput.IsEmpty())
{
sout = stoutput.Pop().ToString() + " " + sout;
}
return sout;
}
public AStack ReturnAStack(string s)
{
string[] strArr = GetStrList(s);
for (int i = 0; i < strArr.Length; i++)
{
tk.Treat(strArr[i]);
}
while (!sttemp.IsEmpty())
{
stoutput.Push(sttemp.Pop());
}
return stoutput;
}
private string[] GetStrList(string s)
{
string [] strArr=s.Split(" ".ToCharArray());
return strArr;
}
}
}
MyOperatoer.cs
using System;
using System.Collections;
using System.Text;
namespace ChainofResposibility.EX24_3
{
/**//// <summary>
/// MyOperatoer 操作符和优先级
/// </summary>
class MyOperatoer
{
private string _ope;
private int _level;
public MyOperatoer(string o,int l)
{
_ope=o;
_level = l;
}
public string OP
{
get{ return _ope; }
set { _ope = value; }
}
public int Level
{
get { return _level; }
set { _level = value; }
}
public override string ToString()
{
return _ope;
}
}
}
------------------------------------
CompToken.cs
using System;
using System.Collections;
using System.Text;
using ChainofResposibility.EX24_3;
namespace ChainofResposibility.EX24_4
{
abstract class CompToken:TreatToken
{
protected string token = "";
public CompToken(AStack st, AStack stout)
: base(st, stout)
{
}
public override void Treat(string s)
{
if (s == token)
{
double b = Convert.ToDouble(this.stOutput.Pop().ToString());
double a = Convert.ToDouble(this.stOutput.Pop().ToString());
double c = Com(a, b);
this.stOutput.Push(c.ToString());
}
else
{
this.Next.Treat(s);
}
}
public abstract double Com(double a, double b);
}
}
AddToken.cs
using System;
using System.Collections;
using System.Text;
using ChainofResposibility.EX24_3;
namespace ChainofResposibility.EX24_4
{
class AddToken:CompToken
{
public AddToken(AStack st,AStack stout)
: base(st,stout)
{
token = "+";
}
public override double Com(double a, double b)
{
return a + b;
}
}
}
SubtractionToken.cs
using System;
using System.Collections;
using System.Text;
using ChainofResposibility.EX24_3;
namespace ChainofResposibility.EX24_4
{
class SubtractionToken : CompToken
{
public SubtractionToken(AStack st, AStack stout)
: base(st,stout)
{
token = "-";
}
public override double Com(double a, double b)
{
return a - b;
}
}
}
MultipToken.cs
using System;
using System.Collections;
using System.Text;
using ChainofResposibility.EX24_3;
namespace ChainofResposibility.EX24_4
{
class MultipToken : CompToken
{
public MultipToken(AStack st, AStack stout)
: base(st,stout)
{
token = "*";
}
public override double Com(double a, double b)
{
return a * b;
}
}
}
DivisionToken.cs
using System;
using System.Collections;
using System.Text;
using ChainofResposibility.EX24_3;
namespace ChainofResposibility.EX24_4
{
class DivisionToken:CompToken
{
public DivisionToken(AStack st, AStack stout)
: base(st,stout)
{
token = "/";
}
public override double Com(double a, double b)
{
return a / b;
}
}
}
ModToken.cs
using System;
using System.Collections;
using System.Text;
using ChainofResposibility.EX24_3;
namespace ChainofResposibility.EX24_4
{
/**//// <summary>
/// ModToken オトユェメェヒオテ」
/// </summary>
class ModToken:CompToken
{
public ModToken(AStack st, AStack stout)
: base(st,stout)
{
token = "%";
}
public override double Com(double a, double b)
{
return a%b;
}
}
}
static void Main(string[] args)
{
//得到后则表达式
string s = "( 2 + 3 ) / 3";
Parser p = new Parser();
Console.WriteLine(s);
Console.WriteLine(p.Do(s));
Parser px = new Parser();
//计算后则表达式
Computer c = new Computer(px.ReturnAStack(s));
Console.WriteLine(c.Do());
Console.ReadLine();
}
相关模式
职责链模式经常和组合模式一起使用,在这种情况下父 组件可以作为其在职责链上的后继。