值得永久收藏的 C# 设计模式套路(三)

设计模式套路,完结篇。

今天写写设计模式套路中的最后一部分:行为设计模式。

这是这个系列的最后一篇。前两篇在:

值得永久收藏的 C# 设计模式套路(一)

值得永久收藏的 C# 设计模式套路(二)

如果你没有看过前两篇,建议先去看看前两篇,讲的是设计模式中,创造性设计和结构设计两种模式中的套路。

行为设计模式跟前两种模式从内容上是有区别的。行为设计模式更关注对象之间的通信,以及职责和任务的交互。

一、责任链

名称起得很明显, 就是一个链式的责任或任务。为什么要链式呢?是因为请求要沿着多个处理程序往后传递。一个任务,可能要分很多步,又不想把所有的步骤耦合到一个处理程序中处理,就会用到这个套路。

看看代码:

public interface IHandler
{public IHandler SetNext(IHandler handler);public object Handle(object input);
}
public class Handler : IHandler
{private IHandler _handler;public IHandler SetNext(IHandler handler){_handler = handler;return handler;}public virtual object Handle(object input){return _handler?.Handle(input);}
}
public class HandlerA : Handler
{public override object Handle(object input){if (input as string == "A"){Console.WriteLine("HandlerA : just return");return true;}Console.WriteLine("HandlerA : call next handler");return base.Handle(input);}
}
public class HandlerB : Handler
{public override object Handle(object input){if (input as string == "B"){Console.WriteLine("HandlerB : just return");return true;}Console.WriteLine("HandlerB : call next handler");return base.Handle(input);}
}
public class HandlerC : Handler
{public override object Handle(object input){if (input as string == "C"){Console.WriteLine("HandlerC : just return");return true;}Console.WriteLine("HandlerC : end");return base.Handle(input);}
}
public static class Example
{public static void Test(){var handlerA = new HandlerA();var handlerB = new HandlerB();var handlerC = new HandlerC();handlerA.SetNext(handlerB).SetNext(handlerC);var resultOne = handlerA.Handle("A");var resultTwo = handlerA.Handle("B");var resultThree = handlerA.Handle("C");var resultFour = handlerA.Handle("D");}// results A:// HandlerA : just return// results B:// HandlerA : call next handler// HandlerB : just return// results C:// HandlerA : call next handler// HandlerB : call next handler// HandlerC : just return// results D:// HandlerA : call next handler// HandlerB : call next handler// HandlerC : end
}

这里面,重要的是 handlerA.SetNext(handlerB).SetNext(handlerC) 一句。这个在限定链的方向和内容。能理解到这一层,就算是真懂了。

二、命令

这个网上内容很多,Command,通常会跟 Delegate、Event 一起说。

咱们这儿单说这个命令模式。

命令模式是一个非常常用的模式。它的作用,是把请求转换为对象,以便我们可以异步、延迟、队列或者参数化请求,以及做一些可撤销的工作。

代码套路特别简单:

public interface ICommand
{public void Execute();
}
public class DemoCommand : ICommand
{private readonly string _parameter;public DemoCommand(string parameter){_parameter = parameter;}public void Execute(){Console.WriteLine(_parameter);}
}
public static class Invoker
{public static void SendAction(ICommand command){command.Execute();}
}
public static class Example
{public static void Test(){var command = new DemoCommand("Hello WangPlus");Invoker.SendAction(command);}// results:// Hello WangPlus
}

这个 Command 的应用场景特别多,建议大家理解透彻。我们在做 SDK 或 类库的时候,会经常有类库内实现业务逻辑,而调用端实现数据交互的情况,用的就是命令模式。举个例子说:做个认证授权的类库,库里面去实现鉴权和生成 Token 的工作,调用端去判断登录帐号密码的验证。这样做,这个库才能是一个跟数据库和帐号体系无关的通用库。

三、迭代器

这也是一个用得很多的模式。

它最主要的作用,就是遍历集合的元素;而最主要的特性,就是不会暴露数据本身。

看代码:

public abstract class IteratorBase
{public abstract bool EndOfDocument();public abstract object Next();public abstract object First();public abstract object Current();
}
public class Iterator : IteratorBase
{private readonly List<object> _customList;private int current = 0;public Iterator(List<object> customList){_customList = customList;}public override bool EndOfDocument(){if (current >= _customList.Count - 1) return true;return false;}public override object Current(){return _customList[current];}public override object Next(){if (current < _customList.Count - 1) return _customList[++current];return null;}public override object First(){return _customList[0];}
}
public static class Example
{public static void Test(){var demolist = new List<object>() { "a", "b", "c", "d" };var iterator = new Iterator(demolist);var item = iterator.First();while (item != null){Console.WriteLine(item);item = iterator.Next();}if (iterator.EndOfDocument()) Console.WriteLine("Iterate done");}//results:// a// b// c// d// Iterate done
}

如果想了解迭代器的原理、异步及更深的应用,可以去看看我专门讲迭代器的文章一文说通C#中的异步迭代器

四、解释器

这也是行为模式中一个常用的模式,它主要是根据上下文来获取不同的类型和行为。换句话说,相同的内容,针对不同的类型,采取不同的行为。

通常这个模式,用得最多的是多语言的场景。

public class Context
{public string Value{get;private set;}public Context(string value){Value = value;}
}
public abstract class Interpreter
{public abstract void Interpret(Context context);
}
public class EnglishInterpreter : Interpreter
{public override void Interpret(Context context){switch (context.Value){case "1":Console.WriteLine("One");break;case "2":Console.WriteLine("Two");break;}}
}
public class ChineseInterpreter : Interpreter
{public override void Interpret(Context context){switch (context.Value){case "1":Console.WriteLine("一");break;case "2":Console.WriteLine("二");break;}}
}
public static class Example
{public static void Test(){var interpreters = new List<Interpreter>() {new EnglishInterpreter(),new ChineseInterpreter()};var context = new Context("2");interpreters.ForEach(c => c.Interpret(context));}// results:// two// 二
}

上面这个例子是解释器的标准套路。通常我们用的时候,可以配合抽象工厂模式,根据上下文独立加载单个的解释器,这样就能实现类似根据浏览器的设定语言来显示界面语言的代码。

如果用微软的标准库来实现,那这个解释器和抽象工厂已经被包在了库里,使用时只需定义语言对照表就成。但内里的逻辑,还是这个。

五、中介

注意,是中介,不是中间件。这是两个东西,别用混了。

不过,两个原理上有一点相像。中介模式,目的是解耦对象之间的直接通信,并转为从中介对象来传递消息。

也是看代码:

public interface IMediator
{public void Send(string message, Caller caller);
}
public class Mediator : IMediator
{public CallerA CallerA{get;set;}public CallerB CallerB{get;set;}public void Send(string message, Caller caller){if (caller.GetType() == typeof(CallerA)){CallerB.ReceiveRequest(message);}else{CallerA.ReceiveRequest(message);}}
}
public abstract class Caller
{protected readonly IMediator _mediator;public Caller(IMediator mediator){_mediator = mediator;}
}
public class CallerA : Caller
{public void SendRequest(string msg){_mediator.Send(msg, this);}public void ReceiveRequest(string msg){Console.WriteLine("CallerA Received : " + msg);}public CallerA(IMediator mediator) : base(mediator) { }
}
public class CallerB : Caller
{public void SendRequest(string msg){_mediator.Send(msg, this);}public void ReceiveRequest(string msg){Console.WriteLine("CallerB Received : " + msg);}public CallerB(IMediator mediator) : base(mediator) { }
}
public static class Example
{public static void Test(){var mediator = new Mediator();var callerA = new CallerA(mediator);var callerB = new CallerB(mediator);mediator.CallerA = callerA;mediator.CallerB = callerB;callerA.SendRequest("Hello");callerB.SendRequest("WangPlus");}// results:// CallerB Received : Hello// CallerA Received : WangPlus
}

CallerA 和 CallerB 之间没有直接通信,而是经由中介 Mediator 进行了消息传递。

这个模式,最常用的场景是两个现成的类库之间要实现通讯,而不想或没办法修改这两个类库的代码,就可以做一个中介库,来进行数据传递。

六、备忘录

跟名字一样。备忘录模式主要是用来保存对象的状态,并将状态封装,以便在需要时,恢复到前边的状态。

套路是这样的:

public class Memento
{private readonly string _state;public Memento(string state){_state = state;}public string GetState(){return _state;}
}
public class Originator
{public string State{get;set;}public Originator(string state){State = state;}public Memento CreateMemento(){return new Memento(State);}public void RestoreState(Memento memento){State = memento.GetState();}
}
public class Taker
{private Memento _memento;public void SaveMemento(Originator originator){_memento = originator.CreateMemento();}public void RestoreMemento(Originator originator){originator.RestoreState(_memento);}
}public static class Example
{public static void Test(){var originator = new Originator("First State");var careTaker = new Taker();careTaker.SaveMemento(originator);Console.WriteLine(originator.State);originator.State = "Second State";Console.WriteLine(originator.State);careTaker.RestoreMemento(originator);Console.WriteLine(originator.State);}// results:// First State// Second State// First State
}

这个代码看着复杂,其实核心就一点:在改变状态前,先把状态在对象之外保存下来。

你细品。

七、观察者

观察者模式主要处理的是对象间一对多的通信。如果一个对象的状态发生了变化,依赖对象会发出通知并进行更新。

public class Updater
{public string NewState{get;}private readonly List<ObserverBase> _observers = new List<ObserverBase>();public Updater(string newState){NewState = newState;}public void AddObserver(ObserverBase observerBase){_observers.Add(observerBase);}public void BroadCast(){foreach (var observer in _observers){observer.Update();}}
}
public abstract class ObserverBase
{public abstract void Update();
}
public class Observer : ObserverBase
{private readonly string _name;public string State;private readonly Updater _updater;public Observer(string name, string state, Updater updater){_name = name;State = state;_updater = updater;}public override void Update(){State = _updater.NewState;Console.WriteLine($"Observer {_name} State Changed to : " + State);}
}
public static class Example
{public static void Test(){var updater = new Updater("WangPlus");updater.AddObserver(new Observer("1", "WangPlus1", updater));updater.AddObserver(new Observer("2", "WangPlus2", updater));updater.AddObserver(new Observer("3", "WangPlus3", updater));updater.BroadCast();}// results:// Observer 1 State Changed to : WangPlus// Observer 2 State Changed to : WangPlus// Observer 3 State Changed to : WangPlus
}

好吧,这个代码各上面备忘录模式的代码有点像。事实上,这两个模式的主要区别,一个是一对一,一个是一对多。

至于为什么两个模式的名称区别这么大,说实话,我也不知道。在我的概念中,这两个模式是可以混着用的。经验来说,备忘录模式我用得更多些。

八、状态

状态模式也是一个常用的模式。

状态模式,和最前面的责任链模式,两个有点类似。状态模式更直接,就是状态改变时,同步改变行为。

这两个模式,在很多情况下,可以有效减少 if…else 的分支数量。所以,看上去就又高大上了:)

上套路:

public interface IState
{public void Handle(Context context);
}
public class StateA : IState
{public void Handle(Context context){context.State = new StateB();}
}
public class StateB : IState
{public void Handle(Context context){context.State = new StateA();}
}
public class Context
{private IState _state;public IState State{get => _state;set{_state = value;Console.WriteLine("State: " + _state.GetType().Name);}}public Context(IState state){State = state;}public void Action(){State.Handle(this);}
}
public static class Example
{public static void Test(){var context = new Context(new StateA());context.Action();context.Action();context.Action();context.Action();}// results:// State: StateA// State: StateB// State: StateA// State: StateB// State: StateA
}

看懂了吗?

如果把里面的 IState 换成 IHandler,那就是一个责任链。区别就是一个是数据,一个是方法,除此之外都一样。

所以,还是我老说的那句话:不要关心名称,要关心实质。在现代开发中,数据、方法、对象,其实都在趋向大一统的。明白这个道理,你就通了。

九、策略

策略模式主要是用来封装算法族并使它们可互换,所以它们可以独立地进行更改,而不需要任何紧密耦合。

对,又是一个以解耦为目的的架构。

public interface IStrategy
{public void AlgorithmAction();
}
public class AlgorithmStrategyA : IStrategy
{public void AlgorithmAction(){Console.WriteLine("This is Algorithm A");}
}
public class AlgorithmStrategyB : IStrategy
{public void AlgorithmAction(){Console.WriteLine("This is Algorithm B");}
}
public class Context
{private readonly IStrategy _strategy;public Context(IStrategy strategy){_strategy = strategy;}public void GeneralAction(){_strategy.AlgorithmAction();}
}
public static class Example
{public static void Test(){var context = new Context(new AlgorithmStrategyA());context.GeneralAction();context = new Context(new AlgorithmStrategyB());context.GeneralAction();}// results:// This is Algorithm A// This is Algorithm A
}

这个模式的核心是上下文中的 IStrategy,这本身是一个抽象,这个抽象对应的实体里,才是算法的实现。

一个标准的模式。

十、模板

模板模式是面向对象的一个基础模式,应用也非常广。

这个模式类似于抽象工厂模式,在基类上建立整体的操作结构,并根据需求的变动,在子类中重写一些操作。

听着很复杂,其实代码一看就明白:

public abstract class TemplateBase
{public void Operate(){FirstAction();SecondAction();}private void FirstAction(){Console.WriteLine("First action from template base");}protected virtual void SecondAction(){Console.WriteLine("Second action from template base");}
}
public class TemplateA : TemplateBase { }
public class TemplateB : TemplateBase
{protected override void SecondAction(){Console.WriteLine("Second action from template B");}
}
public class TemplateC : TemplateBase
{protected override void SecondAction(){Console.WriteLine("Second action from template B");}
}
public static class Example
{public static void Test(){var templateMethodA = new TemplateA();var templateMethodB = new TemplateB();var templateMethodC = new TemplateC();templateMethodA.Operate();templateMethodB.Operate();templateMethodC.Operate();}// results:// First action from template base// Second action from template base// First action from template base// Second action from template B// First action from template base// Second action from template B
}

很简单,对吧?

十一、访问者

访问者模式,是上面模板模式的一个变形,目的一样,在不修改基类代码的情况下,向子类的层次结构中添加新的方法和行为。

看代码:

public interface IVisitor
{public void VisitItem(ItemBase item);
}
public class VisitorA : IVisitor
{public void VisitItem(ItemBase item){Console.WriteLine("{0} visited by {1}", item.GetType().Name, this.GetType().Name);}
}
public class VisitorB : IVisitor
{public void VisitItem(ItemBase item){Console.WriteLine("{0} visited by {1}", item.GetType().Name, this.GetType().Name);}
}
public abstract class ItemBase
{public abstract void Accept(IVisitor visitor);
}
public class ItemA : ItemBase
{public override void Accept(IVisitor visitor){visitor.VisitItem(this);}public void ExtraOperationA() { }
}
public class ItemB : ItemBase
{public override void Accept(IVisitor visitor){visitor.VisitItem(this);}public void ExtraOperationB() { }
}
public class StructureBuilder
{readonly List<ItemBase> _items = new List<ItemBase>();public void AddItem(ItemBase element){_items.Add(element);}public void Accept(IVisitor visitor){foreach (var item in _items){item.Accept(visitor);}}
}
public static class Example
{public static void Test(){var structure = new StructureBuilder();structure.AddItem(new ItemA());structure.AddItem(new ItemB());structure.Accept(new VisitorA());structure.Accept(new VisitorB());}//results://ItemA visited by VisitorA//ItemB visited by VisitorA//ItemA visited by VisitorB//ItemB visited by VisitorB
}

访问者模式扩展了模板模式,扩展性、复用性、灵活性更好,而且非常体现单一职责原则。

十二、总结

模式套路这就算是写完了,居然用了三篇文章才写完。

有没有感觉?所有的模式,都是为了解耦。解耦的目的,是为了把一个系统分成更细的组件。细分组件更适合大团队的开发。而大团队的技术架构,更容易在网上的各种文章中有所体现。

所以,也跟大家提个醒:所有的架构都是需要熟悉和掌握的,这叫面试必备的知识。在实际应用中,如果架构熟悉,所有的程序看着会更富有逻辑,而如果不熟悉架构,那看使用架构写出来的代码,会是一场恶梦。

成长在于一点点的积累,于大家,于我,都一样。

喜欢就来个三连,让更多人因你而受益

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/294860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sv队列和动态数组的区别_Go 刷 LeetCode 系列:经典(7) 设计双端队列

设计实现双端队列。你的实现需要支持以下操作&#xff1a;MyCircularDeque(k)&#xff1a;构造函数,双端队列的大小为k。insertFront()&#xff1a;将一个元素添加到双端队列头部。如果操作成功返回 true。insertLast()&#xff1a;将一个元素添加到双端队列尾部。如果操作成功…

js 中声明变量 “提前”

1.变量定义提升:声明语句&#xff1a;(1)var声明语句&#xff1a;变量声明语句会被“提前”至脚本或者函数的顶部&#xff0c;但是初始化的操作则还在原来var语句的位置执行。例如&#xff0c;下面的例子&#xff0c;所示&#xff1a;function fun(){alert(x);var x666;alert(x…

运维自动化之使用PHP+MYSQL+SHELL打造私有监控系统(二)

现在开始介绍phpmysqlshell监控系统 1、目的此监控系统主要是通过phpmysqlshell的方式&#xff0c;通过shell脚本对各个机器的其各个服务进行监控&#xff0c;达到及时的了解其各个应用服务的状态&#xff08;如果宕掉与启动&#xff09;&#xff0c;在检测应用服务宕掉时&…

Android之事件总线EventBus详解

顾名思义&#xff0c;AndroidEventBus是一个Android平台的事件总线框架&#xff0c;它简化了Activity、Fragment、Service等组件之间的交互&#xff0c;很大程度上降低了它们之间的耦合&#xff0c;使我们的代码更加简洁&#xff0c;耦合性更低&#xff0c;提升了我们的代码质量…

湘乡江南计算机学校,湘乡职业中等专业学校2021年招生录取分数线

许多学生对自己的职业生涯并没有什么明确的规划,所以没有什么好结果,纯属正常现象。只要不断学习,才能不断收获。由此,本网老师为大家整理了湘乡职业中等专业学校2021年招生录取分数线的相关内容,后期若有变化,一切以官方发布为准。湘乡职业中等专业学校往年参考分数线年份地区…

实验4

#include<stdio.h> int main(void) {double r,h,v,n;printf("Enter r,h and n ");scanf("%lf%lf%lf",&r,&h,&n);if(r<0||h<0){printf("输入错误&#xff0c;重新输入");}else{vcylinder(r,h,n);printf("v%.3f\n&qu…

当女朋友问你会不会出轨的时候,该怎么回答?

1 大象为什么会害怕体型小的动物&#xff1f;&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 学会说话很重要&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 原来&#xff0c;他们的老爸是一串香肠&#xff1f;&#xff08;素材来源网络&#xff0c;侵…

WPF开源项目:WPF-ControlBase

仓库截图仓库README很素&#xff0c;但看作者README贴的几篇博文介绍&#xff0c;你会喜欢上它的&#xff0c;废话不多说&#xff0c;上介绍目录&#xff1a;动画封装https://blog.csdn.net/u010975589/article/details/95974854属性表单https://blog.csdn.net/u010975589/arti…

Win10系统修改MAC地址

本地管理地址&#xff0c;输入想修改的MAC地址后&#xff0c;点确定即完成修改。在CMD窗口中&#xff0c;使用ipconfig 命令可以查看新的MAC地址。 再次钩选不存在&#xff0c;则还原为原来的MAC地址。

ftp上传图片出现550_FtpClient 实现文件上传

FtpUtils 工具类封装 public static boolean uploadFile( String hostname, int port, String username, String password, String pathname, String remote,InputStream local) { boolean flagfalse; try{ //创建 FtpClient 对象 FTPClient clientnew FTPClient…

Android之安全退出应用程序的几种方式

当我们做项目的时候,当用户在几秒的时间之内按回车键的时候,需要退出程序,但是退出我们要确保安全退出,防止还有程序还在后台运行,下面介绍几种安全的退出程的几种方式(综合了其它博客的然后加上自己使用的看到的总结) number1: 首先获取当前进程的id,然后杀死该进程。…

西电计算机应用基础 一,15秋西电《计算机应用基础(一)》在线作业答案解析.doc...

西电《计算机应用基础(一)》在线作业一、单选题(共 25 道试题&#xff0c;共 100 分。)1. 下拉式菜单命令项右侧的三角形标记说明&#xff1a;. 该命令项当前正在起作用。. 选择该命令将弹出一个对话框。. 该命令项是级联式命令。. 该命令项无快捷键组合。正确答案&#xff1a;…

SBuild 0.1.4 发布,基于 Scala 的构建系统

SBuild 0.1.4 改进了 Eclipse 插件的稳定性&#xff1b;ZipSchemeHandler的 TargetFile 参数相对于项目目录&#xff1b;一些 Ant 的封装提供更多支持的参数。 SBuild 是基于 Scala 的构建系统&#xff0c;主要特点&#xff1a; 平台无关支持多项目自动检测所需的动作以及新版本…

ZOJ 3228(AC自动机+修改的匹配)

题目大意&#xff1a;给出一个字符串和若干个单词&#xff0c;问这些单词在字符串里面出现了多少次。单词前面为0表示这个单词可重叠出现&#xff0c;1为不可重叠出现。 分析&#xff1a;可重叠出现的单词可以直接用ac自动机就解决。至于不可重叠的单词&#xff0c;可以用一个数…

一篇论文未发博士毕业,中科院最年轻院士入职浙大

全世界只有3.14 % 的人关注了爆炸吧知识本文由科研大匠&#xff08;Id:keyandajiang&#xff09;综合整理自科技日报、网络、科研大匠等11月30日&#xff0c;浙江大学官微转载《浙江日报》头版文章消息提到&#xff0c;“目前中国最年轻的中科院院士孙斌勇已入职数学高等研究院…

C# WPF MVVM开发框架Caliburn.Micro常用功能指南②

这是Caliburn.Micro项目中最常用的约定和功能的快速指南。01—事件连接这会自动将控件上的事件关联到ViewModel上的方法。常规约定&#xff1a;<Button x:Name"Save">这将导致按钮的单击事件调用ViewModel上的“Save”方法。简短语法&#xff1a;<Button ca…

4.7、Bootstrap V4自学之路------组件---广告屏

为什么80%的码农都做不了架构师&#xff1f;>>> 示例 单独的一个空的标签 <div class"jumbotron"><!-- 背景色是灰色的--> <div> PS&#xff1a;可以看出来&#xff0c;其中上下边距还是挺高的。 <div class"jumbotron"&…

计算机和hdmi无法正常显示,HDMI都不灵 为什么电脑连电视效果差?

1电脑连接电视用法人群庞大【中关村在线显示器频道原创】目前的桌面级显示器尺寸最大的范围就是30英寸&#xff0c;但是30英寸的显示器产品价格过于昂贵&#xff0c;因此很少有消费者能够选择购买。因此&#xff0c;目前大部分消费者都会购买27英寸的显示器&#xff0c;但是问题…

easyui 修改单元格内容_初学Excel办公软件快速修改文字的方法

今天我们学习Excel办公软件快速修改文字的方法&#xff0c;首先我们看这个表格里面的文字很多都是相差一个字&#xff0c;甚至很多内容相差不大&#xff0c;因此我们在输入文字时就需要改进快速方法了。首先我们根据图片来操作&#xff0c;我们修改红色字体里的数据&#xff0c…

Android display架构分析

这篇文章非常好&#xff0c;必须转载。目录(?)[-] Kernel Space Display架构介绍函数和数据结构介绍函数和数据结构介绍函数和数据结构介绍数据流分析初始化过程分析User Space display接口Kernel display接口典型应用flow分析介绍 Surface manager&#xff08;surface flinge…