本文主要是介绍GOF23种设计模式系列之行为型设计模式(C#实现),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
GOF23种设计模式系列之行为型设计模式
目录
- GOF23种设计模式系列之行为型设计模式
- 什么是行为型设计模式
- 解释器模式 (InterpreterPattern)
- 调用方式
- 如果正确使用解释器模式呢?
- Template Method(模板方法)
- 调用方式
- 关于模板方法
- Chain of Responsibility(责任链)
- 调用方式
- 关于责任链模式
- Command(命令)
- 调用方式
- 关于命令模式
- Iterator(迭代器)
- 调用方式
- 关于迭代器
- Mediator(中介者)
- 调用方式
- 关于中介者模式
- Memento(备忘录)
- 调用方式
- 关于Memento(备忘录)模式
- Observer(观察者)
- 集合模式
- 调用方式
- 委托模式
- 调用方式
- 关于Observer(观察者)模式
- State(状态)
- 调用方式
- 关于状态模式
- Strategy(策略)
- 抽象方式
- 反射方式
- 关于Strategy(策略)
- Visitor(访问者)
- 调用方式
- 关于Visitor(访问者)模式
什么是行为型设计模式
11种行为型设计模式,是最大的一个家族了。
行为型设计模式关注的是对象和行为的分离,直白点说就是行为(方法)是放在这个类里面,还是那个类里面,关注的内容更细腻,因此套路也更多!
行为型设计模式有以下几种:
- Interpreter(解释器)
- Template Method(模板方法)
- Chain of Responsibility(责任链)
- Command(命令)
- Iterator(迭代器)
- Mediator(中介者)
- Memento(备忘录)
- Observer(观察者)
- State(状态)
- Strategy(策略)
- Visitor(访问者)
解释器模式 (InterpreterPattern)
定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的 “语言” 是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
public abstract class BaseInterpreter{public abstract void Conversion(Context context);}
/// <summary>/// 上下文/// </summary>public class Context{private string _Word = null;public Context(string word){this._Word = word;}public void Set(string newWord){this._Word = newWord;}public string Get(){return this._Word;}}
public class JNInterpreter : BaseInterpreter{private static Dictionary<char, string> _Dictionary = new Dictionary<char, string>();static JNInterpreter(){_Dictionary.Add('j', "a");_Dictionary.Add('k', "b");_Dictionary.Add('l', "c");_Dictionary.Add('m', "d");_Dictionary.Add('n', "e");}public override void Conversion(Context context){string text = context.Get();if (string.IsNullOrEmpty(text))return;List<string> numberList = new List<string>();foreach (var item in text.ToLower().ToArray()){if (_Dictionary.ContainsKey(item)){numberList.Add(_Dictionary[item]);}else{numberList.Add(item.ToString());}}context.Set(string.Concat(numberList));}}public class AIInterpreter : BaseInterpreter{private static Dictionary<char, string> _Dictionary = new Dictionary<char, string>();static AIInterpreter(){_Dictionary.Add('a', "1");_Dictionary.Add('b', "2");_Dictionary.Add('c', "3");_Dictionary.Add('d', "4");_Dictionary.Add('e', "5");_Dictionary.Add('f', "6");_Dictionary.Add('g', "7");_Dictionary.Add('h', "8");_Dictionary.Add('i', "9");}public override void Conversion(Context context){string text = context.Get();if (string.IsNullOrEmpty(text))return;List<string> numberList = new List<string>();foreach (var item in text.ToLower().ToArray()){if (_Dictionary.ContainsKey(item)){numberList.Add(_Dictionary[item]);}else{numberList.Add(item.ToString());}}context.Set(string.Concat(numberList));}}public class ZeroInterpreter : BaseInterpreter{private static Dictionary<char, string> _Dictionary = new Dictionary<char, string>();static ZeroInterpreter(){_Dictionary.Add('1', "1");_Dictionary.Add('2', "2");_Dictionary.Add('3', "3");_Dictionary.Add('4', "4");_Dictionary.Add('5', "5");_Dictionary.Add('6', "6");_Dictionary.Add('7', "7");_Dictionary.Add('8', "8");_Dictionary.Add('9', "9");}public override void Conversion(Context context){string text = context.Get();if (string.IsNullOrEmpty(text))return;List<string> numberList = new List<string>();foreach (var item in text.ToLower().ToArray()){if (!_Dictionary.ContainsKey(item)){numberList.Add("0");}else{numberList.Add(item.ToString());}}context.Set(string.Concat(numberList));}}
调用方式
{Context context = new Context("ObjectIsNotFound");List<BaseInterpreter> interpreterList = new List<BaseInterpreter>(){new JNInterpreter(),new AIInterpreter(),new ZeroInterpreter()};foreach (var item in interpreterList){item.Conversion(context);}Console.WriteLine(context.Get());}
如果正确使用解释器模式呢?
解释器模式通常用来解释一种语言。
优点:1. 解释器其实就是简单的语法分析工具,最显著的优点就是它的拓展性,语法规则可以随意的修改,随便定制自己需要的解释器。缺点:1. 解释器会有很多个,规则越多,解释器越多,当然解释器的子类会非常的多。2. 上层需要循环解释算法,下层需要循环每一个数据,性能比较低。
Template Method(模板方法)
通过继承,抽象,重写等方式,将相同的业务交给父类实现,将不同的业务交给子类实现。
通过声明父类并用子类实现的方式,定义通用处理流程并实现了通用部分,为可变部分留作扩展点
下面以银行的业务举例,银行的理财客户包含不同的人,使用模板方法实现不同的财务逻辑。
/// <summary>/// 银行客户端模拟/// </summary>public abstract class BaseClient{/// <summary>/// 登陆查询功能/// </summary>/// <param name="id"></param>/// <param name="name"></param>/// <param name="password"></param>public void Query(int id, string name, string password){if (this.CheckUser(id, password)){double balance = this.QueryBalance(id);double interest = this.CalculateInterest(balance);this.Show(name, balance, interest);}else{Console.WriteLine("账户密码错误");}}/// <summary>/// 用户检测/// </summary>/// <param name="id"></param>/// <param name="password"></param>/// <returns></returns>public bool CheckUser(int id, string password){return DateTime.Now < DateTime.Now.AddDays(1);}/// <summary>/// 查询余额/// </summary>/// <param name="id"></param>/// <returns></returns>public double QueryBalance(int id){return new Random().Next(10000, 1000000);}/// <summary>/// 获取利率,计算利息/// 定存 0.05/// 活期 0.03/// 再增加一个/// </summary>/// <param name="balance"></param>/// <returns></returns>public abstract double CalculateInterest(double balance);/// <summary>/// 展示下/// /// </summary>/// <param name="name"></param>/// <param name="balance"></param>/// <param name="interest"></param>public virtual void Show(string name, double balance, double interest){Console.WriteLine("尊敬的{0}客户,你的账户余额为:{1},利息为{2}",name, balance, interest);}}
/// <summary>/// 银行客户端模拟 活期客户/// </summary>public class ClientCurrent: BaseClient{/// <summary>/// 获取利率,计算利息/// 定存 0.05/// 活期 0.03/// 再增加一个/// </summary>/// <param name="balance"></param>/// <returns></returns>public override double CalculateInterest(double balance){return balance * 0.03;}}/// <summary>/// 银行客户端模拟 定存客户/// </summary>public class ClientRegular : BaseClient{/// <summary>/// 获取利率,计算利息/// 定存 0.05/// 活期 0.03/// 再增加一个/// </summary>/// <param name="balance"></param>/// <returns></returns>public override double CalculateInterest(double balance){return balance * 0.05;}}/// <summary>/// 银行客户端模拟 VIP客户/// </summary>public class ClientVip : BaseClient{/// <summary>/// /// </summary>/// <param name="balance"></param>/// <returns></returns>public override double CalculateInterest(double balance){return balance * 0.07;}public override void Show(string name, double balance, double interest){Console.WriteLine("尊贵的{0}客户,你的账户余额为:{1},利息为{2}",name, balance, interest);Console.WriteLine("理财有风险,入行需谨慎");}}
调用方式
{Console.WriteLine("****************ClientCurrent****************");BaseClient current = new ClientCurrent();current.Query(234, "张三", "123");Thread.Sleep(100);}{Console.WriteLine("****************ClientCurrent****************");BaseClient regular = new ClientRegular();regular.Query(345, "李四", "456");Thread.Sleep(100);}{Console.WriteLine("****************ClientVip****************");BaseClient regular = new ClientVip();regular.Query(345, "王五", "789");Thread.Sleep(100);}
关于模板方法
模板方法在我们为项目编写框架时经常见,例如我们经常用的MVC,Winfrom,Webfrom等框架中的常用方法都包含这种设计模式
例如:controllerBase等
Chain of Responsibility(责任链)
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到最后一个对象处理完为止。
下面这个例子将表达一个员工申请请假后的请假流程。其中项目经理能审批8小时内的假,主管能审批16小时,经理能审批32小时,总监能审批64小时,CEO能审批120小时
/// <summary>/// 审批者抽象父类/// </summary>public abstract class AbstractAuditor{public string Name { get; set; }public abstract void Audit(ApplyContext context);private AbstractAuditor _NextAuditor = null;public void SetNext(AbstractAuditor auditor){this._NextAuditor = auditor;}protected void AuditNext(ApplyContext context){if (this._NextAuditor != null){this._NextAuditor.Audit(context);}}}/// <summary>/// 请假申请上下文/// 请假条---Context---上下文---保存请求的各种参数-中间值-结果/// /// </summary>public class ApplyContext{public int Id { get; set; }public string Name { get; set; }/// <summary>/// 请假时长/// </summary>public int Hour { get; set; }/// <summary>/// 请假描述/// </summary>public string Description { get; set; }/// <summary>/// 审批结果,默认是false/// </summary>public bool AuditResult { get; set; }/// <summary>/// 审批备注/// </summary>public string AuditRemark { get; set; }}
/// <summary>/// 项目经理/// 职责:/// 1 权限范围内审批通过/// 2 超出权限就转交给主管/// /// 组织架构变更---下一环节换人---改代码---不稳定--怎么稳定一下---甩锅大法---弄哪里不稳定就丢给别人,自己稳定就行----方法初始化/// </summary>public class PM : AbstractAuditor{public override void Audit(ApplyContext context){Console.WriteLine("这里是项目经理 {0} 审批", this.Name);if (context.Hour <= 8){context.AuditResult = true;context.AuditRemark = "enjoy your vacation!";}else{base.AuditNext(context);}}}/// <summary>/// 主管/// </summary>public class Charge: AbstractAuditor{//public string Name { get; set; }public override void Audit(ApplyContext context){Console.WriteLine("这里是主管 {0} 审批", this.Name);if (context.Hour <= 16){context.AuditResult = true;context.AuditRemark = "enjoy your vacation!";}else{base.AuditNext(context);}}}/// <summary>/// 经理/// </summary>public class Manager : AbstractAuditor{public override void Audit(ApplyContext context){Console.WriteLine("这里是经理 {0} 审批", this.Name);if (context.Hour <= 32){context.AuditResult = true;context.AuditRemark = "enjoy your vacation!";}else{base.AuditNext(context);}}}/// <summary>/// 总监/// </summary>public class Chif : AbstractAuditor{//public string Name { get; set; }public override void Audit(ApplyContext context){Console.WriteLine("这里是总监 {0} 审批", this.Name);if (context.Hour <= 64){context.AuditResult = true;context.AuditRemark = "enjoy your vacation!";}else{base.AuditNext(context);}}}/// <summary>/// CEO/// </summary>public class CEO : AbstractAuditor{public override void Audit(ApplyContext context){Console.WriteLine("这里是CEO {0} 审批", this.Name);if (context.Hour <= 128){context.AuditResult = true;context.AuditRemark = "enjoy your vacation!";}else{base.AuditNext(context);}}}
调用方式
{Console.WriteLine("****************************************");AbstractAuditor pm = new PM(){Name = "PM"};AbstractAuditor charge = new Charge(){Name = "Charge"};AbstractAuditor manager = new Manager(){Name = "Manager"};AbstractAuditor chif = new Chif(){Name = "Chif"};AbstractAuditor ceo = new CEO(){Name = "CEO"};pm.SetNext(charge);charge.SetNext(manager);manager.SetNext(chif);chif.SetNext(ceo);pm.SetNext(ceo);pm.Audit(context);}
上面的审批只是让大家理解责任链的原理,具体编写审批功能肯定需要考虑到“加签”,“退回”,“多人”等复杂模式,需要配合反射,配置等方式重新设计
关于责任链模式
责任链模式主要使用在业务逻辑连续,频繁变动,互相依赖度较高的场景。
优点:
1. 降低相互依赖,降低耦合度
2. 逻辑更加灵活可拓展
3. 符合开闭原则缺点:
4. 容易造成反复调用造成死循环
5. 审批链较长时,影响性能
6. 容易忽略一些未编写的问题,导致出现bug
Command(命令)
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
以下例子讲述Client(客户端)操作时,Receiver(接收器),Invoker(调用者),Command(命令)的动作场景
public abstract class BaseCommand{public IReceiver _Receiver{get;private set;}public void SetReiceiver(IReceiver receiver){this._Receiver = receiver;}public abstract void Excute();}public interface IReceiver{void Read();void Write();void Save();}public class Receiver : IReceiver{public void Read(){//log好命令Console.WriteLine("Read1");}public void Write(){Console.WriteLine("Write1");}public void Save(){Console.WriteLine("Save1");}}public class Invoker{private BaseCommand _BaseCommand = null;//可以换成多个命令的集合public Invoker(BaseCommand baseCommand){this._BaseCommand = baseCommand;}public void Excute(){this._BaseCommand.Excute();}}
<appSettings><add key="r" value="CommandPattern.SaveCommand,CommandPattern"/><add key="w" value="CommandPattern.WriteCommand,CommandPattern"/>
</appSettings>
调用方式
//input r/w
string input = Console.ReadLine();
string action = ConfigurationManager.AppSettings[input];//通过反射创建对象
BaseCommand command = (BaseCommand)Activator.CreateInstance(action.Split(',')[1], action.Split(',')[0]).Unwrap();//接收器,一般为主要处理业务的服务。
IReceiver receiver = new ReceiverNew();//也可以配置+反射
command.SetReiceiver(receiver);Invoker invoker = new Invoker(command);
invoker.Excute();
关于命令模式
为每个角色提供丰富的拓展,一般使用在金融,大数据等需要在每个节点添加日志,统计等功能的系统中。
优点
1. 降低耦合度,为每一角色提供丰富的拓展性
2. 满足“开闭原则”,修改功能不受影响
3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
缺点
1.可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
Iterator(迭代器)
对于不同的集合,提供一个通用的访问方式,支持按需获取
以下例子通过对菜单的遍历,展示迭代器的设计方式
public interface IIterator<T>{T Current { get; }bool MoveNext();void Reset();}
public class MacDonaldMenu{private List<Food> _FoodList = new List<Food>();public MacDonaldMenu(){this._FoodList.Add(new Food(){Id = 1,Name = "鸡肉卷",Price = 15});this._FoodList.Add( new Food(){Id = 2,Name = "红豆派",Price = 10});this._FoodList.Add(new Food(){Id = 3,Name = "薯条",Price = 9});}public List<Food> GetFoods(){return this._FoodList;}public IIterator<Food> GetIterator(){return new MacDonaldMenuIterator(this);}}
/// <summary>/// 使用foreach的条件是 类型包含public IEnumerator GetEnumerator()/// </summary>public class Food //: IEnumerable{public int Id { get; set; }public string Name { get; set; }public int Price { get; set; }}
调用方式
MacDonaldMenu menu = new MacDonaldMenu();IIterator<Food> iterator = menu.GetIterator();while (iterator.MoveNext()){Food food = iterator.Current;Console.WriteLine("{0} {1} {2}¥", food.Id, food.Name, food.Price);}
关于迭代器
c# 平时使用的循环遍历有for,foreach。array获取个数的方式只有Length,List获取个数的方式只有count。因为有类似这种的限制,想要统一访问就需要设计迭代器提供统一途径。
按需分配需要了解 yield return,每一次MoveNext()的时候只返回一个数据,不会一次性把所有的数据都返回。
Mediator(中介者)
使用中介者模式来集中相关对象之间复杂的沟通和控制方式,使得这些对象不必相互明显引用。从而使它们可以较松散地耦合。当这些对象中的某些对象之间的相互作用发生改变时,不会立即影响到其他的一些对象之间的相互作用。从而保证这些相互作用可以彼此独立地变化。
下面的例子描述通过中介在模式实现发消息和收消息的功能
public class BaseMediator{private List<BaseCharacter> _BaseCharacterList = new List<BaseCharacter>(); public void AddCharacter(BaseCharacter baseCharacter){this._BaseCharacterList.Add(baseCharacter);} /// <summary>/// 群里面发消息的功能/// </summary>/// <param name="message"></param>/// <param name="characterFrom"></param>public void SendMessage(string message, BaseCharacter characterFrom){Console.WriteLine("{0}Send:{1}", characterFrom.Name, message);foreach (var item in this._BaseCharacterList){item.GetMessage(message, characterFrom);} } }
BaseCharacter teacher = new Teacher(){Name = "老师"};BaseCharacter master = new Master(){Name = "班长"};BaseCharacter student1 = new Student(){Name = "学生1"};BaseCharacter student2 = new Student(){Name = "学生2"};BaseCharacter student3 = new Student(){Name = "学生3"};BaseCharacter student4 = new Student(){Name = "学生4"};BaseCharacter student5 = new Student(){Name = "学生5"};
调用方式
{BaseMediator mediator = new BaseMediator();mediator.AddCharacter(teacher);mediator.AddCharacter(master);mediator.AddCharacter(student1);mediator.AddCharacter(student2);mediator.AddCharacter(student3);mediator.AddCharacter(student4);mediator.AddCharacter(student5);mediator.SendMessage("今天晚上八点上课,大家不要错过哦", master); Console.WriteLine("*********************************"); mediator.SendMessage("收到,一定准时前来", student4);}
关于中介者模式
一般使用在一对一或一对多的场景中,或者我们在数据库中点对点,或点对面的设计中使用。比如菜单,人员权限。。。。
使用中介者模式可以把对象之间的交互封装到中介者对象里面,从而使得对象之间松散耦合。而且对象之间的交互都被封装到中介者对象里面集中管理,集中了控制交互。当交互发生改变时,着重修改的是中介者对象。当需要扩展中介者对象时,其他对象不需要做修改。但也有个潜在的缺点,中介者负责的职责越多,修改影响的结构越多,越不稳定。
Memento(备忘录)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态以下例子用游戏保存再读取的方式向大家展示备忘录模式的设计方式
/// <summary>/// 魔兽争霸/// </summary>public class War3{public string Race { get; set; }public string Hero { get; set; }public string Army { get; set; }public string Resource { get; set; }public void Show(){Console.WriteLine("********************************");Console.WriteLine(" Race:{0}", this.Race);Console.WriteLine(" Hero:{0}", this.Hero);Console.WriteLine(" Army:{0}", this.Army);Console.WriteLine("Resource:{0}", this.Resource);Console.WriteLine("********************************");}public void Save(string name){War3Memento memento = new War3Memento(this.Hero, this.Army, this.Resource);Caretaker.SaveWar3Memento(name, memento);}public void Load(string name){War3Memento memento = Caretaker.GetWar3Memento(name);this.Army = memento.Army;this.Hero = memento.Hero;this.Resource = memento.Resource;}}
/// <summary>/// 魔兽争霸备忘/// </summary>public class War3Memento{public string Hero { get; private set; }public string Army { get; private set; }public string Resource { get; private set; }public War3Memento(string hero, string army, string resource){this.Hero = hero;this.Army = army;this.Resource = resource;}}/// <summary>/// 第三方保存容器:txt xml db 缓存 内存/// </summary>public class Caretaker{private static Dictionary<string, War3Memento> _War3MementoDictionary = new Dictionary<string, War3Memento>();public static void SaveWar3Memento(string name, War3Memento war3Memento){_War3MementoDictionary.Add(name, war3Memento);}public static War3Memento GetWar3Memento(string name){if (_War3MementoDictionary.ContainsKey(name))return _War3MementoDictionary[name];elsethrow new Exception("wrong name");}}
调用方式
/// <summary>/// 1 undo和redo:备忘模式和命令模式的区别/// 2 备忘录模式(Memento Patern),一次恢复和多次恢复/// </summary>class Program{static void Main(string[] args){try{War3 war3 = new War3(){Race = "Undead",Hero = "Level 1 DK",Army = "5只食尸鬼",Resource = "200G 200W"};war3.Save("Start");war3.Show();Console.WriteLine("*****MF*******");war3.Hero = "3级DK1级Lich";war3.Army = "5只蜘蛛2只食尸鬼";war3.Resource = "500G 300W";war3.Save("MF");war3.Show();Console.WriteLine("*****交战*******");war3.Hero = "4级DK 3级Lich 1级小强";war3.Army = "1只憎恶6只蜘蛛1只雕像";war3.Resource = "1000G 1500W";war3.Save("交战");Console.WriteLine("*****决战*******");war3.Hero = "5级DK 4级Lich 3级小强";war3.Army = "2只憎恶8只蜘蛛2只雕像";war3.Resource = "1000G 1500W";war3.Save("决战");Console.WriteLine("*****决战后*******");war3.Hero = "6级DK 6级Lich 3级小强";war3.Army = "1只憎恶2只蜘蛛1只冰龙";war3.Resource = "10G 1200W";war3.Save("决战后");war3.Load("Start");war3.Show();war3.Load("MF");war3.Show();war3.Load("交战");war3.Show();war3.Load("决战");war3.Show();war3.Load("决战后");war3.Show();}catch (Exception ex){Console.WriteLine(ex.Message);}Console.Read();}}
关于Memento(备忘录)模式
一般办公`,游戏软件使用较多,当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。`
备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。
在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。
Observer(观察者)
观察者模式又名发布-订阅(publish-Subscribe)模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
以下例子通过猫叫引起的连锁反应掩饰观察者模式的使用
public interface IObserver{void Action();}
public class Baby : IObserver{public void Cry(){Console.WriteLine("{0} Cry", this.GetType().Name);}public void Action(){this.Cry();}}
public class Brother : IObserver{public void Turn(){Console.WriteLine("{0} Turn", this.GetType().Name);}public void Action(){this.Turn();}}
public class Cricket : IObserver{public void Sing(){Console.WriteLine("{0} Sing", this.GetType().Name);}public void Action(){this.Sing();}}
public class Dog : IObserver{public void Wang(string name){Console.WriteLine("{0} Wang", this.GetType().Name);}public void Action(){this.Wang("2");}}
集合模式
public class Cat{private List<IObserver> _ObserverList = new List<IObserver>();public void AddObserver(IObserver observer){this._ObserverList.Add(observer);}public void RemoveObserver(IObserver observer){this._ObserverList.Remove(observer);}public void MiaoObserver(){Console.WriteLine("{0} MiaoObserver 一声", this.GetType().Name);//一系列后续动作foreach (var observer in this._ObserverList){observer.Action();}}}
调用方式
{Brother brother = new Brother();cat.AddObserver(new Baby());cat.AddObserver(brother);cat.AddObserver(new Cricket());cat.AddObserver(new Dog());cat.MiaoObserver(); cat.RemoveObserver(brother);cat.MiaoObserver();}
委托模式
public class Cat{public event Action CatMiaoEvent;public void MiaoEvent(){Console.WriteLine("{0} MiaoEvent 一声", this.GetType().Name);if (this.CatMiaoEvent != null){CatMiaoEvent.Invoke();}}}
调用方式
{Console.WriteLine("*************Event***************");Brother brother = new Brother(); cat.CatMiaoEvent += new Baby().Cry;cat.CatMiaoEvent += brother.Turn;cat.CatMiaoEvent += new Cricket().Sing;cat.CatMiaoEvent += new Mouse().Run;cat.CatMiaoEvent += () => new Dog().Wang("3");cat.MiaoEvent(); cat.CatMiaoEvent -= brother.Turn; cat.MiaoEvent();}
关于Observer(观察者)模式
通过观察者模式可以解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
使用中需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,如果一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
State(状态)
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象( Objects for States)
以下例子通过对红绿灯状态改变的模拟,展示状态性设计模式的使用方式
public abstract class LightBase{public LightColor Color { get; set; }public void Shine(){Console.WriteLine("灯亮着");}public abstract void Show();public abstract void TurnContext(Context context);}
public class Context{public LightBase CurrentLight { get; set; }public void Show(){this.CurrentLight.Show();}public void Turn(){this.CurrentLight.TurnContext(this);}}
public class LightGreen : LightBase{/// <summary>/// 初始化的时候 指定灯的颜色/// </summary>public LightGreen(){base.Color = LightColor.Green;}public override void Show(){Console.WriteLine("绿灯行");}public override void TurnContext(Context context){context.CurrentLight = new LightYellow();}}public class LightRed : LightBase{/// <summary>/// 初始化的时候 指定灯的颜色/// </summary>public LightRed(){base.Color = LightColor.Red;}public override void Show(){Console.WriteLine("红灯停");}public override void TurnContext(Context context){context.CurrentLight = new LightBlue();}}public class LightYellow : LightBase{/// <summary>/// 初始化的时候 指定灯的颜色/// </summary>public LightYellow(){base.Color = LightColor.Yellow;}public override void Show(){Console.WriteLine("黄灯请小心");}public override void TurnContext(Context context){context.CurrentLight = new LightRed();}}
调用方式
LightBase light = new LightGreen();Context context = new Context(){CurrentLight = light};context.Show();context.Turn();context.Show();context.Turn();context.Show();context.Turn();context.Show();context.Turn();
关于状态模式
状态模式是根据不同的状态执行不同的行为,把不同的状态分拆到不同的类中,让每一个类显得更简单,更方便我们维护和拓展。但是会使程序更加复杂。
常用场景有:电流,铁路,刷卡,投票…
Strategy(策略)
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
以下例子通过模仿计算器的计算逻辑展示策略模式的使用。
public interface ICaculation{int Caculation(int iInpuLeft, int iInputRight);}
public class Plus : ICaculation{public int Caculation(int iInpuLeft, int iInputRight){return iInpuLeft + iInputRight;}}public class Mutiply : ICaculation{public int Caculation(int iInpuLeft, int iInputRight){return iInpuLeft * iInputRight;}}public class Minus : ICaculation{public int Caculation(int iInpuLeft, int iInputRight){return iInpuLeft - iInputRight;}}public class Devision : ICaculation{public int Caculation(int iInpuLeft, int iInputRight){if (iInputRight == 0) throw new Exception();return iInpuLeft / iInputRight;}}
/// <summary>/// 面向对象语言开发从来不担心代码多,因为可以封装一下/// 工厂只是转移了矛盾,并没有消除矛盾/// </summary>public class Factory{/// <summary>/// 不仅把对象创建给屏蔽了,而且映射关系也可以配置文件决定了/// </summary>/// <param name="operate"></param>/// <returns></returns>public static ICaculation GetCaculationReflection(string operate){string key = $"ICaculation{operate}";string dllType = ConfigurationManager.AppSettings[key];Assembly assembly = Assembly.Load(dllType.Split(',')[1]);Type type = assembly.GetType(dllType.Split(',')[0]);return (ICaculation)Activator.CreateInstance(type);}public static ICaculation GetCaculation(string operate){ICaculation iCaculation = null;switch (operate){case "+":iCaculation = new Plus();break;case "-":iCaculation = new Minus();break;case "*":iCaculation = new Mutiply();break;case "/":iCaculation = new Devision();break;default:Console.WriteLine("输入符号异常,重新输入");throw new Exception("输入符号异常,重新输入");}return iCaculation;}}
<configuration><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup><appSettings><add key="ICaculation-" value="StrategyPattern.Service.Plus,StrategyPattern.Service"/><add key="ICaculation+" value="StrategyPattern.Service.Minus,StrategyPattern.Service"/><add key="ICaculation*" value="StrategyPattern.Service.Mutiply,StrategyPattern.Service"/><add key="ICaculation/" value="StrategyPattern.Service.Devision,StrategyPattern.Service"/><add key="CaculationType" value="+,-,*,/,%"/></appSettings>
</configuration>
抽象方式
static void Main(string[] args){try{Console.WriteLine("下面是一个计算器展示demo:");while (true){#region UI 前端逻辑 接受用户输入并验证int iInputLeft = 0;int iInputRight = 0;string operate = "";Console.WriteLine("输入第一个数字(整数):");string sInputLeft = Console.ReadLine();if (!int.TryParse(sInputLeft, out iInputLeft)){Console.WriteLine("输入数字无效,请重新输入");continue;}Console.WriteLine("输入计算符号(+-*/):");operate = Console.ReadLine();string CaculationType = System.Configuration.ConfigurationManager.AppSettings["CaculationType"];if (!CaculationType.Split(',').Contains(operate)){Console.WriteLine("输入计算符号无效,请重新输入");continue;}Console.WriteLine("输入第二个数字(整数):");string sInputRight = Console.ReadLine();if (!int.TryParse(sInputRight, out iInputRight)){Console.WriteLine("输入数字无效,请重新输入");continue;}#endregion#region 后台逻辑 业务逻辑int iResult = 0;ICaculation iCaculation = null;switch (operate)//从POP到OOP,屏蔽细节,{case "+":iCaculation = new Plus();break;case "-":iCaculation = new Minus();break;case "*":iCaculation = new Mutiply();break;case "/":iCaculation = new Devision();break;default:Console.WriteLine("输入符号异常,重新输入");continue;}//3 转移了算法创建以及映射关系,封装了一下iCaculation = Factory.GetCaculation(operate);//1 转移了算法逻辑iResult = iCaculation.Caculation(iInputLeft, iInputLeft);Console.WriteLine("计算为: {0}{1}{2}={3}", iInputLeft, operate, iInputRight, iResult);#endregion}}catch (Exception ex){Console.WriteLine(ex.Message);}Console.Read();}
反射方式
static void Main(string[] args){try{Console.WriteLine("下面是一个计算器展示demo:");while (true){Console.WriteLine("******************************");Console.WriteLine("******************************");Console.WriteLine("******************************");#region UI 前端逻辑 接受用户输入并验证int iInputLeft = 0;int iInputRight = 0;string operate = "";Console.WriteLine("输入第一个数字(整数):");string sInputLeft = Console.ReadLine();if (!int.TryParse(sInputLeft, out iInputLeft)){Console.WriteLine("输入数字无效,请重新输入");continue;}Console.WriteLine("输入计算符号(+-*/):");operate = Console.ReadLine();string CaculationType = System.Configuration.ConfigurationManager.AppSettings["CaculationType"];if (!CaculationType.Split(',').Contains(operate)){Console.WriteLine("输入计算符号无效,请重新输入");continue;}Console.WriteLine("输入第二个数字(整数):");string sInputRight = Console.ReadLine();if (!int.TryParse(sInputRight, out iInputRight)){Console.WriteLine("输入数字无效,请重新输入");continue;}#endregion#region 后台逻辑 业务逻辑int iResult = 0;ICaculation iCaculation = null;//3 转移了算法创建以及映射关系,封装了一下iCaculation = Factory.GetCaculationReflection(operate);//1 转移了算法逻辑CaculationContext context = new CaculationContext(iCaculation, iInputLeft, iInputRight);//2 转移了算法的调用逻辑iResult = context.Action();Console.WriteLine("计算为: {0}{1}{2}={3}", iInputLeft, operate, iInputRight, iResult);#endregion}}catch (Exception ex){Console.WriteLine(ex.Message);}Console.Read();}
/// <summary>/// 上下文环境:是为了保存整个请求过程中,全部的信息--中间结果--最终结果/// 行为型设计模式的标配,行为会无止境的到处转移,方法需要参数。执行结果/// /// 包一层:没有什么技术问题是包一层不能解决的,如果有,再包一层/// 中间层,转移调用,核心意义就在于调用环节可以扩展/// </summary>public class CaculationContext{private ICaculation _iCaculation = null;private int _iInpuLeft = 0;private int _iInputRight = 0;public CaculationContext(ICaculation caculation, int iInpuLeft, int iInputRight){this._iCaculation = caculation;this._iInpuLeft = iInpuLeft;this._iInputRight = iInputRight;}private string Para = "";//可能要调用第三方接口/// <summary>/// 也许调用算法,需要额外的参数信息/// </summary>/// <returns></returns>public int Action(){try{Console.WriteLine("Caculation");Console.WriteLine(this.Para);return this._iCaculation.Caculation(this._iInpuLeft, this._iInputRight);}catch (Exception ex){Console.WriteLine(ex.Message);throw;}}}
关于Strategy(策略)
策略模式应对业务处理中,会有多种相似处理方式(算法),然后封装成算法+抽象,此外,调用环节也有扩展要求的,需要context
好处:算法封装,有抽象可以扩展;
调用环节转移,可以扩展;
缺陷:上端必须知道全部算法,而且知道映射关系
Visitor(访问者)
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
以下例子通过对培训班学生的福利模拟,解释访问者模式的设计方式
public interface IVisitor{void GetVideoFree(StudentFree studentFree);void GetVideoVip(StudentVip studentVip);//void GetVideoVVip(StudentVVip studentVVip);}
/// <summary>/// 学生/// </summary>public abstract class Student{public int Id { get; set; }public string Name { get; set; }public long QQ { get; set; }public void Study(){Console.WriteLine("{0}跟着Eleven老师学习.net高级开发", this.Name);}public abstract void GetVideo();public abstract void GetVideoVisitor(IVisitor visitor);}
/// <summary>/// 访问者:完成获取视频的行为/// 当前福利/// </summary>public class VisitorCurrent : IVisitor{public void GetVideoFree(StudentFree studentFree){Console.WriteLine("只能获取当次课的公开课视频代码");}public void GetVideoVip(StudentVip studentVip){Console.WriteLine("免费获取全套的公开课视频代码合集");}}
/// <summary>/// 访问者:完成获取视频的行为/// 过去的福利/// </summary>public class VisitorPast : IVisitor{public void GetVideoFree(StudentFree studentFree){Console.WriteLine("没有视频只有代码");}public void GetVideoVip(StudentVip studentVip){Console.WriteLine("获取公开课代码合集");}}
/// <summary>/// 访问者:完成获取视频的行为/// 未来的福利/// </summary>public class VisitorTemp : IVisitor{public void GetVideoFree(StudentFree studentFree){Console.WriteLine("进群获取当次课的视频代码");}public void GetVideoVip(StudentVip studentVip){Console.WriteLine("继续免费获取任何视频课件代码");}}
public class StudentVip : Student{ public override void GetVideoVisitor(IVisitor visitor){visitor.GetVideoVip(this);//this 是当前的实例}//再建立多个方法 每个方法对应一种方式//再建立一堆的子类 分别去完成}
public class StudentFree : Student{public override void GetVideoVisitor(IVisitor visitor){visitor.GetVideoFree(this);//this 是当前的实例}//再建立多个方法 每个方法对应一种方式//再建立一堆的子类 分别去完成 }
调用方式
static void Main(string[] args){try{List<Student> studentList = new List<Student>(){new StudentVip(){Id=638,Name="张三"},new StudentVip(){Id=586,Name="李四"},new StudentFree(){Id=123,Name="王五"}};foreach (var student in studentList){Console.WriteLine("*************************");student.Study(); student.GetVideo();}{Console.WriteLine("*************VisitorCurrent************");VisitorCurrent visitor = new VisitorCurrent();foreach (var student in studentList){Console.WriteLine("*************************");student.Study();student.GetVideoVisitor(visitor);}}{Console.WriteLine("*************VisitorPast************");VisitorPast visitor = new VisitorPast();foreach (var student in studentList){Console.WriteLine("*************************");student.Study();student.GetVideoVisitor(visitor);}}{Console.WriteLine("*************VisitorTemp************");VisitorTemp visitor = new VisitorTemp();foreach (var student in studentList){Console.WriteLine("*************************");student.Study();student.GetVideoVisitor(visitor);}}}catch (Exception ex){Console.WriteLine(ex.Message);}Console.Read();}
关于Visitor(访问者)模式
通过对逻辑的转移,实现随意增加逻辑不需要改动student类
一般适用于结构很少变化(类很少变化)但是方法经常变化的时候我们可以将方法转移到Visitor中,保证对象的稳定,让对象不意外行为的变化或者增加行为而去修改。例如:消息处理(多消息进行不同的记录要求)
这篇关于GOF23种设计模式系列之行为型设计模式(C#实现)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!