C#.Net 设计模式学习笔记之行为型 (二)
1、备忘录模式(Memento Pattern)
对象状态的回溯:
对象状态的变化无端,如何回溯/恢复对象在某个点的状态?
动机:
在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。
意图:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后可以将该对象恢复到原先保存的状态。
适用性:
1.必须保存一个对象某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
2.如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
代码实现:
using System; namespace MementoPattern { class Memento { private string name; private string phone; private double budget; //Constructor public Memento(string name, string phone, double budget) { this.name = name; this.phone = phone; this.budget = budget; } //Properties public string Name { get { return name; } set { name = value; } } public string Phone { get { return phone; } set { phone = value; } } public double Budget { get { return budget; } set { budget = value; } } } class ProspectMemory { private Memento memento; //Property public Memento Memento { set { memento = value; } get { return memento; } } } //Originator class SalesProspect { private string name; private string phone; private double budget; //Properties public string Name { get { return name; } set { name = value; Console.WriteLine("Name:" + name); } } public string Phone { get { return phone; } set { phone = value; Console.WriteLine("Phone:" + phone); } } public double Budget { get { return budget; } set { budget = value; Console.WriteLine("Budget:" + budget); } } public Memento SaveMemento() { Console.WriteLine("\nSaving state--\n"); return new Memento(name, phone, budget); } public void RestoreMemento(Memento memento) { Console.WriteLine("\nRestoring state --\n"); this.Name = memento.Name; this.Phone = memento.Phone; this.Budget = memento.Budget; } } class Program { static void Main(string[] args) { SalesProspect s = new SalesProspect(); s.Name = "zhangsan"; s.Phone = "(010)22223333"; s.Budget = 32000.0; //Store internal state ProspectMemory m = new ProspectMemory(); m.Memento = s.SaveMemento(); //Continue changing originator s.Name = "lisi"; s.Phone = "(026)12345678"; s.Budget = 37000.0; //Restore saved state s.RestoreMemento(m.Memento); //Wait for user Console.Read(); } } }
执行上面代码,显示如下图示:
Memento需要注意的几个要点:
1.备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”。
2.在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器的宽接口;一个为其他对象使用的窄接口。
3.在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改进Memento模式。
2、策略模式(Strategy Pattern)
算法与对象的耦合:
对象可能经常需要使用多种不同的算法,但是如果变化频繁,会将类型变得脆弱...
动机:
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
意图:
定义一系统的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
--摘自《设计模式》
适用性:
1.许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
2.需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[H087],可以使用策略模式。
3.算法使用客户不应该知道数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构。
4.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
代码实现:
enum SortType { QuickSort, ShellSort, MergeSort } class Sort { public void SortList(SortType s) { if (s == SortType.QuickSort) { ProcessA(); } else if (s == SortType.ShellSort) { ProcessB(); } else if (s == SortType.MergeSort) { ProcessC(); } Console.WriteLine(); } protected void ProcessA() { Console.WriteLine("QuickSort List"); } protected void ProcessB() { Console.WriteLine("SheelSort List"); } protected void ProcessC() { Console.WriteLine("MergeSort List"); } }
客户端调用:
class DemoTest { class Program { static void Main(string[] args) { Sort sort = new Sort(); sort.SortList(SortType.QuickSort); sort.SortList(SortType.ShellSort); sort.SortList(SortType.MergeSort); } } }
由此可见,由于客户端新增调用方式的选择,就会修改SortType及Sort里的判断语句。在类Sort中会增加if语句的判断,用敏捷软件开发的语言说,你应该闻到了代码的臭味道了,也就是设计模式中说的存在了变化的地方。
重构以上代码,增加一层中间层来处理变化。类结构如下:
//Stategy 表达抽象算法 abstract class SortStrategy { public abstract void Sort(ArrayList list); } //ConcreateStrategy class ShellSort : SortStrategy { public override void Sort(System.Collections.ArrayList list) { list.Sort();//no-implement Console.WriteLine("ShellSorted List"); } } //ConcreateStrategy class MergeSort : SortStrategy { public override void Sort(System.Collections.ArrayList list) { list.Sort(); Console.WriteLine("MergeSort List"); } } //ConcreateStrategy class QuickSort : SortStrategy { public override void Sort(System.Collections.ArrayList list) { list.Sort(); Console.WriteLine("QuickSort List"); } } //Context class SortList { private ArrayList list = new ArrayList(); private SortStrategy sortstrategy; //对象组合 public void SetSortStrategy(SortStrategy sortstrategy) { this.sortstrategy = sortstrategy; } public void Add(string name) { list.Add(name); } public void Sort() { sortstrategy.Sort(list); //Display results foreach (string name in list) { Console.WriteLine(" " + name); } Console.WriteLine(); } }
客户端代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Program { static void Main(string[] args) { SortList studentRecords = new SortList(); studentRecords.Add("Sss"); studentRecords.Add("Rose"); studentRecords.Add("Jack"); studentRecords.Add("Tang"); studentRecords.Add("Orange"); studentRecords.SetSortStrategy(new QuickSort()); studentRecords.Sort(); studentRecords.SetSortStrategy(new ShellSort()); studentRecords.Sort(); studentRecords.SetSortStrategy(new MergeSort()); studentRecords.Sort(); Console.Read(); } }
由此可见,更好地满足开放封闭原则。
Strategy模式的几个要点:
1.Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。所谓封装算法,支持算法的变化。
2.Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
3.与State类似,如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
3、访问者模式(Visitor Pattern)
类层次结构的变化:
类层次结构中可能经常由于引入新的操作,从而将类型变得脆弱...
动机:
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
意图:
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这引起元素的新操作。
结构:
适用性:
1.一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2.需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作"污染"这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3.定义对象结构的类很少改变,但经常需要在结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
代码实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Program { static void Main(string[] args) { //Setup employee collection Employees e = new Employees(); e.Attach(new Clerk()); e.Attach(new Director()); e.Attach(new President()); //Employees are 'visited' e.Accept(new IncomeVisitor()); e.Accept(new VacationVisitor()); //Wait for user Console.Read(); } } interface IVisitor { void Visit(Element element); } //"ConcreateVisitor1" class IncomeVisitor : IVisitor { public void Visit(Element element) { Employee employee = element as Employee; //Provide 10% pay raise employee.Income *= 1.10; Console.WriteLine("{0}{1}'s new income:{2:C}", employee.GetType().Name, employee.Name, employee.Income); } } //"ConcreateVisitor2" class VacationVisitor : IVisitor { public void Visit(Element element) { Employee employee = element as Employee; //Provide 3 extra vacation days Console.WriteLine("{0}{1}'s new vacation days:{2}", employee.GetType().Name, employee.Name, employee.VacationDays); } } class Clerk : Employee { //Constructor public Clerk() : base("Hank", 35000.0, 14) { } } class Director : Employee { //Constructor public Director() : base("Rose", 38000.0, 16) { } } class President : Employee { //Constructor public President() : base("Tang", 55000.0, 23) { } } //"Element" abstract class Element { public abstract void Accept(IVisitor visitor); } //"ConcreateElement class Employee : Element { string name; double income; int vacationDays; //Constructor public Employee(string name, double income, int vacationDays) { this.name = name; this.income = income; this.vacationDays = vacationDays; } //Properties public string Name { get { return name; } set { name = value; } } public double Income { get { return income; } set { income = value; } } public int VacationDays { get { return vacationDays; } set { vacationDays = value; } } public override void Accept(IVisitor visitor) { visitor.Visit(this); } } //"ObjectStracture" class Employees { private ArrayList employees = new ArrayList(); public void Attach(Employee employee) { employees.Add(employee); } public void Detach(Employee employee) { employees.Remove(employee); } public void Accept(IVisitor visitor) { foreach (Employee e in employees) { e.Accept(visitor); } Console.WriteLine(); } }
执行显示结果如下图所示:
Visoitr模式的几个要点:
1.Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
2.所谓双重分发却Visotor模式中间包括了两个多态分发(注意其中的多态机制);第一个为accept方法的多态辨析;第二个为visitor方法的多态辨析。
3.Visotor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visiotr模式适用"Element"类层次结构稳定,而其中的操作却经常面临频繁改动"。
4、观察者模式(Observer Pattern)
动机(Motivate):
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”
--------一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
意图(Intent):
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
--摘自《设计模式》
结构图(Struct):
适用性:
1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
生活中的例子:
观察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。在ATM取款,当取款成功后,以手机、邮件等方式进行通知。
代码实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class BankAccount { Emailer emailer;//强信赖关系 Mobile phoneNumber;//强信赖关系 private double _money; public Emailer Emailer { get { return emailer; } set { this.emailer = value; } } public Mobile PhoneNumber { get { return phoneNumber; } set { this.phoneNumber = value; } } public double Money { get { return _money; } set { this._money = value; } } public void WithDraw() { emailer.SendEmail(this); phoneNumber.SendNotification(this); } } public class Emailer { private string _emailer; public Emailer(string emailer) { this._emailer = emailer; } public void SendEmail(BankAccount ba) { //.. Console.WriteLine("Notified:Emailer is {0},You withdraw {1:C}", _emailer, ba.Money); } } public class Mobile { private long _phoneNumber; public Mobile(long phoneNumber) { this._phoneNumber = phoneNumber; } public void SendNotification(BankAccount ba) { Console.WriteLine("Notified:Phone number is {0} You withdraw {1:C}", _phoneNumber, ba.Money); } }
此时简单的客户端调用如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class DemoTest { class Program { static void Main(string[] args) { BankAccount ba = new BankAccount(); Emailer emailer = new Emailer("aaa@163.com"); Mobile mobile = new Mobile(13688991010); ba.Emailer = emailer; ba.PhoneNumber = mobile; ba.Money = 3000; ba.WithDraw(); Console.ReadLine(); } } }
执行结果如下图所示:
由此可见程序可以正常运行,但请注意BandAccount和Emailer及Mobile之间形成了一种双向的依赖关系,即BankAccount调用了Emailer及Mobile的方法,而Emailer及Mobile调用了BnadAccount类的属性。如果有其中一个类变化,有可能会引起另一个的变化。如果又需添加一种新的通知方式,就得在BankAccount的WithDraw()方法中增加对该中通知方式的调用。
显然这样的设计极大的违背了“开放-封闭”原则,这不是我们所想要的,仅仅是新增加了一种通知对象,就需要对原有的BankAccount类进行修改,这样的设计是很糟糕的。对此做进一步的抽象,既然出现了多个通知对象,我们就为这些对象之间抽象出一个接口,用它来取消BankAccount和具体的通知对象之间依赖。
由此我们由左图转换到右图。
实例代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface IObserverAccount { void Update(BankAccount ba); } public class BankAccount { IObserverAccount emailer;//依赖于接口 IObserverAccount phoneNumber;//依赖于接口 private double _money; public IObserverAccount Emailer { get { return emailer; } set { this.emailer = value; } } public IObserverAccount PhoneNumber { get { return phoneNumber; } set { this.phoneNumber = value; } } public double Money { get { return _money; } set { this._money = value; } } } public class Emailer : IObserverAccount { private string _emailer; public Emailer(string emailer) { this._emailer = emailer; } public void Update(BankAccount ba) { //.. Console.WriteLine("Notified:Emailer is {0},You withdraw {1:C}", _emailer, ba.Money); } } public class Mobile : IObserverAccount { private long _phoneNumber; public Mobile(long phoneNumber) { this._phoneNumber = phoneNumber; } public void Update(BankAccount ba) { Console.WriteLine("Notified:Phone number is {0} You withdraw {1:C}", _phoneNumber, ba.Money); } }
客户端与上方相同,其运行结果也相同。但BankAccount增加和删除通知对象时,还需对其进行修改。对此我们再做如下重构,在BankAccount中维护一个IObserver列表,同时提供相应的维护方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class BankAccount { private List<IObserverAccount> Observers = new List<IObserverAccount>(); private double _money; public double Money { get { return _money; } set { this._money = value; } } public void WithDraw() { foreach (IObserverAccount ob in Observers) { ob.Update(this); } } public void AddObserver(IObserverAccount observer) { Observers.Add(observer); } public void RemoverObserver(IObserverAccount observer) { Observers.Remove(observer); } }
此时客户端代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class DemoTest { static void Main(string[] args) { BankAccount ba = new BankAccount(); IObserverAccount emailer = new Emailer("abcdwxc@163.com"); IObserverAccount mobile = new Mobile(13901234567); ba.Money = 2000; ba.AddObserver(emailer); ba.AddObserver(mobile); ba.WithDraw(); } }
走到这一步,已经有了Observer模式的影子了,BankAccount类不再依赖于具体的Emailer或Mobile,而是依赖于抽象的IObserverAccount。存在着的一个问题是Emailer或Mobile仍然依赖于具体的BankAccount,解决这样的问题很简单,只需要再对BankAccount类做一次抽象。如下图:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public abstract class Subject { private List<IObserverAccount> Observers = new List<IObserverAccount>(); private double _money; public Subject(double money) { this._money = money; } public double Money { get { return _money; } } public void WithDraw() { foreach (IObserverAccount ob in Observers) { ob.Update(this); } } public void AddObserver(IObserverAccount observer) { Observers.Add(observer); } public void RemoverObserver(IObserverAccount observer) { Observers.Remove(observer); } } public interface IObserverAccount { void Update(Subject subject); } public class BankAccount : Subject { public BankAccount(double money) : base(money) { } } public class Emailer : IObserverAccount { private string _emalier; public Emailer(string emailer) { this._emalier = emailer; } public void Update(Subject subject) { Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emalier, subject.Money); } } public class Mobile : IObserverAccount { private long _phoneNumber; public Mobile(long phoneNumber) { this._phoneNumber = phoneNumber; } public void Update(Subject subject) { Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, subject.Money); } }
此时客户端实现如下:
class Test { static void Main(string[] args) { Subject subject = new BankAccount(3000); subject.AddObserver(new Emailer("aaa@163.com")); subject.AddObserver(new Mobile(13688991010)); subject.WithDraw(); } }
推模式与拉模式
对于发布-订阅模型,大家都很容易能想到推模式与拉模式,用SQL Server做过数据库复制的朋友对这一点很清楚。在Observer模式中同样区分推模式和拉模式,我先简单的解释一下两者的区别:推模式是当有消息时,把消息信息以参数的形式传递(推)给所有观察者,而拉模式是当有消息时,通知消息的方法本身并不带任何的参数,是由观察者自己到主体对象那儿取回(拉)消息。知道了这一点,大家可能很容易发现上面我所举的例子其实是一种推模式的Observer模式。我们先看看这种模式带来了什么好处:当有消息时,所有的观察者都会直接得到全部的消息,并进行相应的处理程序,与主体对象没什么关系,两者之间的关系是一种松散耦合。但是它也有缺陷,第一是所有的观察者得到的消息是一样的,也许有些信息对某个观察者来说根本就用不上,也就是观察者不能“按需所取”;第二,当通知消息的参数有变化时,所有的观察者对象都要变化。鉴于以上问题,拉模式就应运而生了,它是由观察者自己主动去取消息,需要什么信息,就可以取什么,不会像推模式那样得到所有的消息参数。
拉模式实现如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public abstract class Subject { private List<IObserverAccount> Observers = new List<IObserverAccount>(); private double _money; public double Money { get { return _money; } } public Subject(double money) { this._money = money; } public void WithDraw() { foreach (IObserverAccount ob in Observers) { ob.Update(); } } public void AddObserver(IObserverAccount observer) { Observers.Add(observer); } public void RemoverObserver(IObserverAccount observer) { Observers.Remove(observer); } } public interface IObserverAccount { void Update(); } public class BankAccount : Subject { public BankAccount(double money) : base(money) { } } public class Emailer : IObserverAccount { private string _emalier; private Subject _subject; public Emailer(string emailer, Subject subject) { this._emalier = emailer; this._subject = subject; } public void Update() { //.. Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emalier, _subject.Money); } } public class Mobile : IObserverAccount { private long _phoneNumber; private Subject _subject; public Mobile(long phoneNumber, Subject subject) { this._phoneNumber = phoneNumber; this._subject = subject; } public void Update() { Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, _subject.Money); } }
此时客户端调用如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Test { static void Main(string[] args) { Subject subject = new BankAccount(2000); subject.AddObserver(new Emailer("abcdwxc@163.com", subject)); subject.AddObserver(new Mobile(13901234567, subject)); subject.WithDraw(); } }
.NET中Observer实现:
用事件和委托来实现Observer模式。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Subject { public event NotifyEventHandler NotifyEvent; private double _money; public Subject(double money) { this._money = money; } public double Money { get { return _money; } } public void WithDraw() { OnNotifyChange(); } public void OnNotifyChange() { if (NotifyEvent != null) { NotifyEvent(this); } } } public class Emailer { private string _emalier; public Emailer(string emailer) { this._emalier = emailer; } public void Update(object obj) { if (obj is Subject) { Subject subject = (Subject)obj; Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emalier, subject.Money); } } } public delegate void NotifyEventHandler(object sender);
客户端调用如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Test { static void Main(string[] args) { Subject subject = new Subject(3000); Emailer emailer = new Emailer("aaa@163.com"); subject.NotifyEvent += new NotifyEventHandler(emailer.Update); subject.WithDraw(); } }
Observer实现要点:
1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。
2.目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知。目标对象对此一无所知。
3.在C#中的Event。委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象,委托是比抽象Observer接口更为松耦合的设计。
5、解释器模式(Interpreter Pattern)
动机(Motivate):
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。
在这种情况下,将特定领域的问题表达为某种文法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
意图(Intent):
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
结构图(Struct):
生活中的例子:
适用性:
1.当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
而当存在以下情况时该模式效果最好:
2.该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达工,这样可以节省空间而且还可能节省时间。
3.效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种
形式。例如:正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍
是有用的。
代码实现:
客户端代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Program { static void Main(string[] args) { string roman = "五千四百三十二"; //5432 Context context = new Context(roman); //Build the 'parse tree' ArrayList tree = new ArrayList(); tree.Add(new OneExpression()); tree.Add(new TenExpression()); tree.Add(new HundredExpression()); tree.Add(new ThousandExpression()); //Interpret foreach (Expression exp in tree) { exp.Interpret(context); } Console.WriteLine("{0} = {1}", roman, context.Data); //Wait for user Console.Read(); } }
创建一个抽象类Expression,来描述共同的操作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public abstract class Expression { protected Dictionary<string, int> table = new Dictionary<string, int>(9); public Expression() { table.Add("一", 1); table.Add("二", 2); table.Add("三", 3); table.Add("四", 4); table.Add("五", 5); table.Add("六", 6); table.Add("七", 7); table.Add("八", 8); table.Add("九", 9); } public virtual void Interpret(Context context) { if (context.Statement.Length == 0) { return; } foreach (string key in table.Keys) { int value = table[key]; if (context.Statement.EndsWith(key + GetPostifix())) { context.Data += value * Multiplier(); context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength()); } if (context.Statement.EndsWith("零")) { context.Statement = context.Statement.Substring(0, context.Statement.Length - 1); } if (context.Statement.Length == 0) { return; } } } public abstract string GetPostifix(); public abstract int Multiplier(); public virtual int GetLength() { return this.GetPostifix().Length + 1; } }
然后创建一个公共类Context,定义一些全局信息。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Context { private string statement; private int data; //Constructor public Context(string statement) { this.statement = statement; } //Properties public string Statement { get { return statement; } set { statement = value; } } public int Data { get { return data; } set { data = value; } } } public class OneExpression : Expression { public override string GetPostifix() { return ""; } public override int Multiplier() { return 1; } public override int GetLength() { return 1; } } public class TenExpression : Expression { public override string GetPostifix() { return "十"; } public override int Multiplier() { return 10; } public override int GetLength() { return 2; } } public class HundredExpression : Expression { public override string GetPostifix() { return "百"; } public override int Multiplier() { return 100; } public override int GetLength() { return 2; } } public class ThousandExpression : Expression { public override string GetPostifix() { return "千"; } public override int Multiplier() { return 1000; } public override int GetLength() { return 2; } }
Interpreter实现要点:
Interpreter模式的应用场合是interpreter模式应用中的难点,只有满足"业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则的问题"才适合使用Interpreter模式。
使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。
Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。
6、职责链模式(Chain of Responsibility Pattern)
动机(Motivate):
在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。
如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。
意图(Intent):
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
结构图(Struct):
适用性:
1.有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
2.你想在不明确接收者的情况下,向多个对象中的一个提交一个请求。
3.可处理一个请求的对象集合应被动态指定。
生活中的例子:
代码实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//Handler abstract class Approver { protected Approver successor; public void SetSuccessor(Approver successor) { this.successor = successor; } public abstract void ProcessRequest(Purchase purchase); } //ConcreteHandler class Director : Approver { public override void ProcessRequest(Purchase purchase) { if (purchase.Amount < 10000.0) { Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number); } else if (successor != null) { successor.ProcessRequest(purchase); } } } class VicePresident : Approver { public override void ProcessRequest(Purchase purchase) { if (purchase.Amount < 25000.0) { Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number); } else if (successor != null) { successor.ProcessRequest(purchase); } } } class President : Approver { public override void ProcessRequest(Purchase purchase) { if (purchase.Amount < 100000.0) { Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number); } else { Console.WriteLine("Request! {0} requires an executive meeting!", purchase.Number); } } } //Request details 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; } //Properties public double Amount { get { return amount; } set { amount = value; } } public string Purpose { get { return purpose; } set { purpose = value; } } public int Number { get { return number; } set { number = value; } } }
客户端调用如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Program { static void Main(string[] args) { //Setup Chain of Responsibility Director Larry = new Director(); VicePresident Sam = new VicePresident(); President Tammy = new President(); Larry.SetSuccessor(Sam); Sam.SetSuccessor(Tammy); //Generate and process purchase requests Purchase p = new Purchase(1034, 350.00, "Supplies"); 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); //Wait for user Console.Read(); } }
Chain of Responsibility实现要点:
1.Chain
of Responsibility模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只胡一个”,只有这时候请求发送者与接受者的耦合才胡可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
2.应用了Chain of Responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
3.如果请求传递到职责链的未尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。
7、中介者模式(Mediator Pattern)
依赖关系的转化:
动机(Motivate):
在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。
意图(Intent):
用一个中介对象来封装一系列对象交互。中介者使各对象不需要相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
--摘自《设计模式》
结构图(Struct):
适用性:
1.一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
2.一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3.想定制一个分布在多个类中的行为,而又不想生成太多的子类。
代码实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//Mediator abstract class AbstractChatroom { public abstract void Register(Participant participant); public abstract void Send(string from, string to, string message); } //ConcreteMediator class Chatroom : AbstractChatroom { private Hashtable participants = new Hashtable(); public override void Register(Participant participant) { if (participants[participant.Name] == null) { participants[participant.Name] = participant; } participant.Chatroom = this; } public override void Send(string from, string to, string message) { Participant pto = (Participant)participants[to]; if (pto != null) { pto.Receive(from, message); } } } //AbstractColleague class Participant { private Chatroom chatroom; private string name; //Constructor public Participant(string name) { this.name = name; } //Properties public string Name { get { return name; } } public Chatroom Chatroom { set { chatroom = value; } get { return chatroom; } } public void Send(string to, string message) { chatroom.Send(name, to, message); } public virtual void Receive(string from, string message) { Console.WriteLine("{0} to {1}:'{2}'", from, name, message); } } //ConcreteColleaguel class Beatle : Participant { //Constructor public Beatle(string name) : base(name) { } public override void Receive(string from, string message) { Console.Write("To a Beatle: "); base.Receive(from, message); } } //ConcreteColleague2 class NonBeatle : Participant { //Constructor public NonBeatle(string name) : base(name) { } public override void Receive(string from, string message) { Console.Write("To a non-Beatle:"); base.Receive(from, message); } }
客户端调用如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static void Main(string[] args) { //create chatroom Chatroom chatroom = new Chatroom(); //Create participants and register them Participant George = new Beatle("George"); Participant Paul = new Beatle("Paul"); Participant Ringo = new Beatle("Ringo"); Participant John = new Beatle("John"); Participant Yoko = new Beatle("Yoko"); chatroom.Register(George); chatroom.Register(Paul); chatroom.Register(Ringo); chatroom.Register(John); chatroom.Register(Yoko); //chatting participants Yoko.Send("John", "Hi John"); Paul.Send("Ringo", "All you need is love"); Ringo.Send("George", "My sweet Lord"); Paul.Send("John", "Can't buy me love"); John.Send("Yoko", "My sweet love"); }
Mediator实现要点:
1.将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变“多个对象互相关系”为多“个对象和一个中介者关联”,简化了系统的维护,抵御了可能的变化。
2.随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。
这时候可以对Mediator对象进行分解处理。
3.Facade模式是解耦系统外到系统内(单向)的对象关系关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。
8、状态模式(State Pattern)
对象状态影响对象行为:
对象拥有不同的状态,往往会行使不同的行为...
动机:
在软件构建过程中,某些对象的状态如果改变以及其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
如何在运行时根据对象的状态来透明更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?
意图:
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。
--摘自《设计模式》
结构图:
适用性:
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作中含有庞大的多分支的等条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个分支放入一个独立的类中。这使得你可根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
代码实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class MainApp { static void Main() { // Open a new account Account account = new Account("Jim Johnson"); // Apply financial transactions account.Deposit(500.0); account.Deposit(300.0); account.Deposit(550.0); account.PayInterest(); account.Withdraw(2000.00); account.Withdraw(1100.00); // Wait for user Console.Read(); } } // "State" abstract class State { protected Account account; protected double balance; protected double interest; protected double lowerLimit; protected double upperLimit; // Properties public Account Account { get { return account; } set { account = value; } } public double Balance { get { return balance; } set { balance = value; } } public abstract void Deposit(double amount); public abstract void Withdraw(double amount); public abstract void PayInterest(); } // "ConcreteState" // Account is overdrawn class RedState : State { double serviceFee; // Constructor public RedState(State state) { this.balance = state.Balance; this.account = state.Account; Initialize(); } private void Initialize() { // Should come from a datasource interest = 0.0; lowerLimit = -100.0; upperLimit = 0.0; serviceFee = 15.00; } public override void Deposit(double amount) { balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { amount = amount - serviceFee; Console.WriteLine("No funds available for withdrawal!"); } public override void PayInterest() { // No interest is paid } private void StateChangeCheck() { if (balance > upperLimit) { account.State = new SilverState(this); } } } // "ConcreteState" // Silver is non-interest bearing state class SilverState : State { // Overloaded constructors public SilverState(State state) : this(state.Balance, state.Account) { } public SilverState(double balance, Account account) { this.balance = balance; this.account = account; Initialize(); } private void Initialize() { // Should come from a datasource interest = 0.0; lowerLimit = 0.0; upperLimit = 1000.0; } public override void Deposit(double amount) { balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { balance -= amount; StateChangeCheck(); } public override void PayInterest() { balance += interest * balance; StateChangeCheck(); } private void StateChangeCheck() { if (balance < lowerLimit) { account.State = new RedState(this); } else if (balance > upperLimit) { account.State = new GoldState(this); } } } // "ConcreteState" // Interest bearing state class GoldState : State { // Overloaded constructors public GoldState(State state) : this(state.Balance, state.Account) { } public GoldState(double balance, Account account) { this.balance = balance; this.account = account; Initialize(); } private void Initialize() { // Should come from a database interest = 0.05; lowerLimit = 1000.0; upperLimit = 10000000.0; } public override void Deposit(double amount) { balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { balance -= amount; StateChangeCheck(); } public override void PayInterest() { balance += interest * balance; StateChangeCheck(); } private void StateChangeCheck() { if (balance < 0.0) { account.State = new RedState(this); } else if (balance < lowerLimit) { account.State = new SilverState(this); } } } // "Context" class Account { private State state; private string owner; // Constructor public Account(string owner) { // New accounts are 'Silver' by default this.owner = owner; state = new SilverState(0.0, this); } // Properties public double Balance { get { return state.Balance; } } public State State { get { return state; } set { state = value; } } public void Deposit(double amount) { state.Deposit(amount); Console.WriteLine("Deposited {0:C} --- ", amount); Console.WriteLine(" Balance = {0:C}", this.Balance); Console.WriteLine(" Status = {0}\n", this.State.GetType().Name); Console.WriteLine(""); } public void Withdraw(double amount) { state.Withdraw(amount); Console.WriteLine("Withdrew {0:C} --- ", amount); Console.WriteLine(" Balance = {0:C}", this.Balance); Console.WriteLine(" Status = {0}\n", this.State.GetType().Name); } public void PayInterest() { state.PayInterest(); Console.WriteLine("Interest Paid --- "); Console.WriteLine(" Balance = {0:C}", this.Balance); Console.WriteLine(" Status = {0}\n", this.State.GetType().Name); } }
执行结果如下图所示:
State模式的几个要点:
1.State模式将所有一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
2.为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的----即要么彻底转换过来,要么不转换。
3.如果State对象没有实例变量,那么各个上下文可以共享 同一个State对象,从而节省对象开销。