行为模式是对不同的对象之间,划分其责任和算法。
分类:类结构模式或对象结构模式
包含下列模式:
1)不变模式
2)策略模式
3)模板方法模式
4)观察者模式
5)迭代子模式
6)责任链模式
7)命令模式
8)备忘录模式
9)状态模式
10)访问者模式
11)解释器模式
12)调停者模式
一、不变模式
例子:Java里String,Integer, Float等各种封装类
分为:
1)弱不变模式: 子类可能有变化。
2)强不变模式: 子类业务变化或根本不能有子类。
注意:
1)不变模式不是无状态,它可以有状态,只不过状态不变而已。
2)不同于只读属性。例如年龄,即使设置了只读属性,随着时间的变化,其年龄属性也发生变化。
使用方法:
直接在构造的时候,确定状态值,并不允许以后再修改状态。
二、策略模式
例子:以前做的台球算账程序,图书折扣,诸葛亮的锦囊妙计(3个锦囊,3个计策)
目的是封装一组算法,每一个算法是一个具体的策略类。
使用的时候由客户端来决定具体使用哪一个策略类。
三、模板方法模式
例子:好莱坞原则,西天取经八十一难(这81磨难是设计好不能少的,唐僧是具体模板,经历了西游记的81难,换成我去取经也是要经历81难的,第一难,第二难。。。。第81难)
准备一个抽象类,将顶级的逻辑完成,然后声明一些抽象方法来迫使子类来实现其余的逻辑。(这些抽象方法已被抽象类的顶级逻辑所调用)
不同的子类以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。
跟建造模式很像,不过建造模式多了一个director角色,顶级逻辑都放在direcotr角色中。
四、观察者模式
例子:发布-订阅,awt的Listener。
校色有两类,一个是主题,一个是观察者对象,它们是1:N的关系。
当主题的状态发生变化时,可以通知所有的观察者对象。 (调用观察者对象类似Update()的方法)
五、迭代子模式
目的:该模式将迭代逻辑(正序遍历,反序遍历,增加,删除)与聚集本身分开。(聚集是采用vector还是list,都不影响迭代)。
该模式可采用内部类的方式,限制其他类对聚集类索引的访问。(只对迭代类公开)
六、责任链模式
例子:传花击鼓, AWT事件处理,java城堡竞赛
很对对象由每一个对象对其下家的引用而连接起来形成一个链条。
关键点:基类有类似的三个函数
1)是否满足
2)满足执行的一些action
3)取得下家(不满足时)
public class Entry
{
public static void MainProc()
{
var player = new Zhangsan(new Lisi(new Wangwu(null)));
player.Handle("李");
}
}
public interface IPlayer
{
bool IsMeet(string lastName);
IPlayer Next { get; set; }
bool Handle(string lastName);
}
public abstract class Player : IPlayer
{
abstract public bool IsMeet(string lastName);
abstract public void Action();
public Player(IPlayer next)
{
Next = next;
}
public IPlayer Next { get; set; }
public bool Handle(string lastName)
{
if (IsMeet(lastName))
{
Action();
return true;
}
else
{
if (Next == null)
return false;
else
return Next.Handle(lastName);
}
}
}
public class Zhangsan: Player
{
public Zhangsan(IPlayer next) : base(next) { }
public override bool IsMeet(string lastName)
{
return lastName.IndexOf("张") == 0;
}
public override void Action()
{
Console.WriteLine("I am 张三");
}
}
public class Lisi : Player
{
public Lisi(IPlayer next) : base(next) { }
public override bool IsMeet(string lastName)
{
return lastName.IndexOf("李") == 0;
}
public override void Action()
{
Console.WriteLine("I am 李四");
}
}
public class Wangwu : Player
{
public Wangwu(IPlayer next) : base(next) { }
public override bool IsMeet(string lastName)
{
return lastName.IndexOf("王") == 0;
}
public override void Action()
{
Console.WriteLine("I am 王五");
}
}
七、命令模式
重点的对象是命令(抽象对象和具体对象)
简单来说,这个模式是在请求者和接受者之间建立一个command角色,请求者只关心Command, 与接受者解耦。类似于c的callbcack。
例子:太上老君请猴王上天。 请求者太上老君,接受者猴王,命令是执行的圣旨。
再例如Ken用遥控器操纵电视开机,关机,换台:
电视是接受者,有开机,关机,换台三个函数。
实体Command有3个类,调用Execute时,分别调用开机,关机,换台这三个函数。
遥控器是Invoker。Ken是客户端。
public class CommandClient
{
public void Action()
{
var receiver = new Receiver("Monkey King");
var cmd = new ConCommand(receiver);
var invoker = new Invoker(cmd);
invoker.Name = "Tai Bai Jin Xing";
invoker.Action();
}
}
public class Receiver
{
private string Name;
public Receiver(string name)
{
this.Name = name;
}
public void Fly()
{
Console.WriteLine(Name + ": fly to sky.");
}
}
public class ConCommand : ICommand
{
private Receiver receiver;
public ConCommand(Receiver receiver)
{
// TODO: Complete member initialization
this.receiver = receiver;
}
void ICommand.Execute()
{
receiver.Fly();
}
}
public class Invoker
{
private ICommand Cmd;
public Invoker(ICommand cmd)
{
// TODO: Complete member initialization
this.Cmd = cmd;
}
public void Action()
{
if(Cmd != null)
{
Cmd.Execute();
}
}
}
上帝造万物。 抽象命令角色只需要一个借口函数execute(), 具体命令角色:造天command, 造地command, 造人command等。
常会用于回调,undo/redo等场合。
八,备忘录模式
角色: Originator角色,Memento角色,和TakeCare角色。
为了方便理解:
orginator可以理解为manager,使用数据者;
Memento理解为dataModule,
careTaker稍微难理解,就是负责引用dataModule的对象
public class Entry
{
public static void MainProcess()
{
// orginator可以理解为manager或View,使用数据者
var orginator = new Originator();
// Status就是数据
orginator.Status = "On";
var careTaker = new CareTaker();
// Memento可以理解为dataModule,orginator用CreateMemento方法将dataModule创建出来,
// 并当做参数传给careTaker.SaveMemento里
// careTaker这里只起到暂时引用Memento(dataModule)的作用,
// 实际用途中是否可以增加数据序列化的处理??
careTaker.SaveMemento(orginator.CreateMemento()); // very important
// 编辑中
orginator.Status = "Off";
// 放弃编辑,careTaker.RetrieveMemento()将dataModule取出,并当做参数传递给orginator.RestoreMemento()用于restore
orginator.RestoreMemento(careTaker.RetrieveMemento());
}
}
public class Originator
{
public string Status { get; set; }
public Memento CreateMemento()
{
return new Memento(Status);
}
public void RestoreMemento(Memento memento)
{
Status = memento.Status;
}
}
public class Memento
{
public string Status { get; set; }
public Memento(string Status)
{
this.Status = Status;
}
}
public class CareTaker
{
public Memento memento { get; set; }
public void SaveMemento(Memento memento)
{
this.memento = memento;
}
public Memento RetrieveMemento()
{
return memento;
}
}
九, 状态模式
状态抽象类和具体状态类(多个),Context类。
需要考虑由谁负责更改状态,可以是Context类或者是一个具体的状态类。推荐用具体的状态类来切换状态。
重点且接受不了的是:状态类接口定义的行为,例如EmptyStatus::AddPeopel()
近期工作涉及到controller的状态模式,模式是按照以下方式运行:
举例:IdelState->InitState->ConnectedState->RemoteModeState(检测是否支持rmc)->CrcState->UncommittedState->IdelState
十,访问者模式
一个有争议的模式,慎用。该模式是将大量的逻辑写入到visitor里(很多重载函数),而不是通过多态分散到node里。
对象: Vistor对象,Node对象。
实现方式有点像足球来回倒脚。
注意
Node的类型数量最好固定才建议使用该模式,否则重载函数就要相应的增加。
另外如果重载函数太多,不建议使用重载函数,直接通过函数名区分,例如VisitNodeA,VisitNodeB,VisitNodeC...
最好Visitor也是多个的时候使用该模式,例如一个visitor类访问所有的nodes用于计算,另外一个visitor类访问所有的nodes用于打印。
public class Entry
{
public static void MainProc()
{
var nodes = new List();
nodes.Add(new NodeA());
nodes.Add(new NodeB());
nodes.Add(new NodeA());
nodes.Add(new NodeB());
nodes.Add(new NodeB());
nodes.Add(new NodeA());
var visitor = new Visitor();
foreach (var node in nodes)
{
node.Accept(visitor);
}
}
}
public class Visitor
{
public void Visit(NodeA nodeA)
{
nodeA.PrintA();
}
public void Visit(NodeB nodeB)
{
nodeB.PrintB();
}
}
public interface INode
{
void Accept(Visitor visitor);
}
public class NodeA : INode
{
public void Accept(Visitor visitor)
{
visitor.Visit(this);
}
public void PrintA()
{
Console.WriteLine("Print NodeA");
}
}
public class NodeB : INode
{
public void Accept(Visitor visitor)
{
visitor.Visit(this);
}
public void PrintB()
{
Console.WriteLine("Print NodeB");
}
}
十一,解释器模式
用于解释编程"语言" (AST)
每个terminal都有一个interpret()方法。
"And" 这种非termina的Express有leftExpress和rightExpress。
在文章中,并没有看到AST的作用,也没用看到处理terminal和非terminal有什么不同。
例子中"var express = new AND(new Constant(true), new StringBool("true"));" 这不可能出现在"语言"中。
语言写法应类似于: var express = true && "true" ,要转化成上面的写法就需要AST(语法树)
public class Entry
{
public static void MainProc()
{
var express = new AND(new Constant(true), new StringBool("true"));
var ret = express.interfect();
Console.WriteLine(ret.ToString());
}
}
public interface IInterpret
{
bool interfect();
}
public class Constant : IInterpret
{
private bool Value;
public Constant(bool value)
{
Value = value;
}
public bool interfect()
{
return Value;
}
}
public class StringBool : IInterpret
{
private string Value;
public StringBool(string value)
{
Value = value;
}
public bool interfect()
{
return string.Equals("true", Value.ToLower().Trim());
}
}
public class AND : IInterpret
{
IInterpret Left;
IInterpret Right;
public AND(IInterpret left, IInterpret right)
{
Left = left;
Right = right;
}
public bool interfect()
{
return Left.interfect() && Right.interfect();
}
}
十二,调停者模式
角色:调停者Mediator, 各个同事的角色Colleage
慎用该模式,使用该模式,一般说明系统设计较差。
很多Colleage之间的关系混乱,相互引用太多,才考虑使用该模式。
该模式切断Colleage之间的联系,让所有的Colleage只和Mediator交互。
跟门面模式不同的是,该模式交互是双向的。
public class Entry
{
public static void MainProc()
{
var mediator = new Mediator();
var colleage1 = new Colleage1(mediator);
var colleage2 = new Colleage2(mediator);
mediator.Colleage1Inst = colleage1;
mediator.Colleage2Inst = colleage2;
colleage2.Change2();
}
}
public class Colleage1
{
private Mediator MediatorInst { get; set; }
public Colleage1(Mediator mediator)
{
MediatorInst = mediator;
}
public void Change1()
{
MediatorInst.Change(this);
}
public void Action1()
{
Console.WriteLine("Action1 for changing.");
}
}
public class Colleage2
{
private Mediator MediatorInst { get; set; }
public Colleage2(Mediator mediator)
{
MediatorInst = mediator;
}
public void Change2()
{
MediatorInst.Change(this);
}
public void Action2()
{
Console.WriteLine("Action2 for changing.");
}
}
public class Mediator
{
public Colleage1 Colleage1Inst { get; set; }
public Colleage2 Colleage2Inst { get; set; }
public void Change(Colleage1 colleage1)
{
Colleage2Inst.Action2();
}
public void Change(Colleage2 colleage2)
{
Colleage1Inst.Action1();
}
}