本文转载自微信公众号「老王Plus」,收藏设计作者老王Plus的模式老王。转载本文请联系计老王Plus公众号。套路
行为设计模式跟前两种模式从内容上是收藏设计有区别的。行为设计模式更关注对象之间的模式通信,以及职责和任务的套路交互。
名称起得很明显,模式 就是套路一个链式的责任或任务。为什么要链式呢?收藏设计是因为请求要沿着多个处理程序往后传递。一个任务,模式可能要分很多步,套路又不想把所有的收藏设计步骤耦合到一个处理程序中处理,就会用到这个套路。模式
看看代码:
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#中的异步迭代器
这也是行为模式中一个常用的模式,它主要是根据上下文来获取不同的类型和行为。换句话说,相同的内容,针对不同的云南idc服务商类型,采取不同的行为。
通常这个模式,用得最多的是多语言的场景。
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 }访问者模式扩展了模板模式,扩展性、复用性、灵活性更好,而且非常体现单一职责原则。
模式套路这就算是写完了,居然用了三篇文章才写完。
有没有感觉?所有的模式,都是为了解耦。解耦的目的,是为了把一个系统分成更细的组件。细分组件更适合大团队的开发。而大团队的技术架构,更容易在网上的各种文章中有所体现。
所以,也跟大家提个醒:所有的架构都是需要熟悉和掌握的,这叫面试必备的知识。在实际应用中,如果架构熟悉,所有的程序看着会更富有逻辑,而如果不熟悉架构,那看使用架构写出来的代码,会是一场恶梦。
成长在于一点点的积累,于大家,于我,都一样。