设计模式套路,完结篇。
今天写写设计模式套路中的最后一部分:行为设计模式。
这是这个系列的最后一篇。前两篇在:
如果你没有看过前两篇,建议先去看看前两篇,讲的是设计模式中,创造性设计和结构设计两种模式中的套路。
行为设计模式跟前两种模式从内容上是有区别的。行为设计模式更关注对象之间的通信,以及职责和任务的交互。
一、责任链
名称起得很明显, 就是一个链式的责任或任务。为什么要链式呢?是因为请求要沿着多个处理程序往后传递。一个任务,可能要分很多步,又不想把所有的步骤耦合到一个处理程序中处理,就会用到这个套路。
看看代码:
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
}
访问者模式扩展了模板模式,扩展性、复用性、灵活性更好,而且非常体现单一职责原则。
十二、总结
模式套路这就算是写完了,居然用了三篇文章才写完。
有没有感觉?所有的模式,都是为了解耦。解耦的目的,是为了把一个系统分成更细的组件。细分组件更适合大团队的开发。而大团队的技术架构,更容易在网上的各种文章中有所体现。
所以,也跟大家提个醒:所有的架构都是需要熟悉和掌握的,这叫面试必备的知识。在实际应用中,如果架构熟悉,所有的程序看着会更富有逻辑,而如果不熟悉架构,那看使用架构写出来的代码,会是一场恶梦。
成长在于一点点的积累,于大家,于我,都一样。
喜欢就来个三连,让更多人因你而受益