65.9K
CodeProject 正在变化。 阅读更多。
Home

设计模式 3/3 -行为型设计模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (197投票s)

2012年9月7日

CPOL

22分钟阅读

viewsIcon

411507

downloadIcon

6433

在这篇关于设计模式的最后一篇文章中,我将讨论行为设计模式,并将通过实际示例展示如何实现它们。

引言

这是关于设计模式的第三篇(也是最后一篇)文章。在本系列第一篇文章中,我讨论了创建型设计模式。第二篇文章是关于结构型设计模式的,现在我将描述另一组模式,称为行为设计模式。

行为模式解释了对象如何交互。它描述了不同的对象和类如何相互发送消息以实现功能,以及任务的步骤如何划分到不同的对象之间。创建型模式主要描述一个时间点(创建的瞬间),结构型模式描述一个或多或少的静态结构,而行为模式则描述一个过程或流程。

责任链模式

责任链模式是一种设计模式,它定义了一个处理程序的链表,每个处理程序都能够处理请求。当请求提交给链时,它会传递给列表中第一个能够处理它的处理程序。

作为程序员,我确信您遇到过一个问题,即一个对象生成的事件需要由另一个对象处理。责任链模式可以解决这个问题。在这种模式中,我们有一个命令对象源和一系列处理对象。命令被传递给第一个可以处理该命令或将其发送给其后继的处理对象。这个过程一直持续到命令被处理或到达链的末尾。在这种模式中,发送命令的对象不知道哪个对象将处理该命令。

结构代码示例

下面的UML图描述了责任链设计模式的一种实现。

该图由三部分组成

  • 客户端:该类将命令(或请求)传递给处理对象链的第一个对象。
  • HandlerBase:表示所有具体处理程序的接口或基类。它包含一个指向下一个处理对象的成员变量。
  • ConcreteHandlers:这是HandlerBase类的具体实现。
static class Program
{
    static void Main()
    {
        HandlerBase handlerA = new ConcreteHandlerA();
        HandlerBase handlerB = new ConcreteHandlerB();
        handlerA.SetSuccessor(handlerB);
        handlerA.HandleRequest(1);
        handlerB.HandleRequest(11);
    }
}
 
public abstract class HandlerBase
{
    protected HandlerBase _successor;

    public abstract void HandleRequest(int request);

    public void SetSuccessor(HandlerBase successor)
    {
        _successor = successor;
    }
}

public class ConcreteHandlerA : HandlerBase
{
    public override void HandleRequest(int request)
    {
        if (request == 1)
            Console.WriteLine("Handled by ConcreteHandlerA");
        else if (_successor != null)
            _successor.HandleRequest(request);
    }
}


public class ConcreteHandlerB : HandlerBase
{
    public override void HandleRequest(int request)
    {
        if (request > 10)
            Console.WriteLine("Handled by ConcreteHandlerB");
        else if (_successor != null)
            _successor.HandleRequest(request);
    }
}

真实世界示例

让我们看一个真实世界的例子。想象您有一台自动售货机,它接受硬币。机器只有一个投币口,而不是为每种硬币类型设置一个投币口。插入的硬币被发送到由命令接收者确定的适当存储位置。在这个例子中,我们有一个具有两个属性:DiameterWeightCoin类。Coin类在这个例子中是一个请求(或命令)。接下来我们有一个抽象的CoinHandlerBase类,它有一个SetSuccessor()方法用于设置下一个处理对象,以及一个抽象方法EvaluateCoin,该方法将被具体处理程序覆盖。在这个例子中,我们只处理50便士、5便士、1英镑、10便士和20便士的硬币。对于每种硬币类型,我们都必须实现一个处理程序。所以现在我们有五个处理程序。每个处理程序将根据硬币的直径和重量工作,同时尊重处理程序定义的固定值与硬币属性之间的细微差异。这有助于我们解决硬币直径稍大/稍小或硬币稍重或稍轻的问题。

class FiftyPenceHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 8) < 0.02 && Math.Abs(coin.Diameter - 27.3) < 0.15)
        {
            Console.WriteLine("Captured 50p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}

public class FivePenceHandler : CoinHandlerBase
{

    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 3.25) < 0.02 && Math.Abs(coin.Diameter - 18) < 0.1)
        {
            Console.WriteLine("Captured 5p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}

class OnePoundHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 9.5) < 0.02 && Math.Abs(coin.Diameter - 22.5) < 0.13)
        {
            Console.WriteLine("Captured £1");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}
 
public class TenPenceHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 6.5) < 0.03 && Math.Abs(coin.Diameter - 24.5) < 0.15)
        {
            Console.WriteLine("Captured 10p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}

class TwentyPenceHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 5) < 0.01 && Math.Abs(coin.Diameter - 21.4) < 0.1)
        {
            Console.WriteLine("Captured 20p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}
 
public abstract class CoinHandlerBase
{
    protected CoinHandlerBase _successor;

    public void SetSuccessor(CoinHandlerBase successor)
    {
        _successor = successor;
    }

    public abstract CoinEvaluationResult EvaluateCoin(Coin coin);
}

public class Coin
{
    public float Weight { get; set; }
    public float Diameter { get; set; }
}
 
class Program
{
    static void Main()
    {
        var h5 = new FivePenceHandler();
        var h10 = new TenPenceHandler();
        var h20 = new TwentyPenceHandler();
        var h50 = new FiftyPenceHandler();
        var h100 = new OnePoundHandler();
        h5.SetSuccessor(h10);
        h10.SetSuccessor(h20);
        h20.SetSuccessor(h50);
        h50.SetSuccessor(h100);

        var tenPence = new Coin { Diameter = 24.49F, Weight = 6.5F };
        var fiftyPence = new Coin { Diameter = 27.31F, Weight = 8.01F };
        var counterfeitPound = new Coin { Diameter = 22.5F, Weight = 9F };

        Console.WriteLine(h5.EvaluateCoin(tenPence)); 
        Console.WriteLine(h5.EvaluateCoin(fiftyPence));
        Console.WriteLine(h5.EvaluateCoin(counterfeitPound));
    }
}

public enum CoinEvaluationResult
{
    Accepted,
    Rejected
}

命令模式

命令模式是一种设计模式,它允许将请求的所有信息包含在一个对象中。然后可以根据需要调用该命令,通常作为一批排队命令的一部分,并具有回滚功能。

命令模式是一种行为设计模式,其中执行方法所需的所有信息都封装在一个对象中,该对象可以立即使用或保留以供以后使用。这个对象不执行任何操作,它只包含信息。

有三个关键术语需要解释:客户端、调用者和接收者。客户端创建命令对象。调用者决定何时调用需要封装在命令对象中的信息的方法。接收者是包含方法代码的类的实例。

结构代码示例

下面的UML图描述了命令设计模式的一种实现。该图由五部分组成

  • 客户端:该类是命令设计模式的类的消费者。它创建命令对象并将它们链接到接收者。
  • 接收者:该类知道如何执行与执行请求相关的操作。
  • CommandBase:这是所有命令对象的抽象类(或接口)。它包含有关接收者的信息,该接收者负责使用封装在命令对象中的信息执行操作。
  • ConcreteCommand:CommandBase抽象类或接口的具体实现。
  • 调用者:是决定何时执行命令的对象。
class Program
{
    static void Main()
    {
        Client client = new Client();
        client.RunCommand();
    }
}

public class Client
{
    public void RunCommand()
    {
        var invoker = new Invoker();
        var receiver = new Receiver();
        var command = new ConcreteCommand(receiver);
        command.Parameter = "Hello, world!";
        invoker.Command = command;
        invoker.ExecuteCommand();
    }
}

public class Receiver
{
    public void Action(string message)
    {
        Console.WriteLine("Action called with message, '{0}'.", message);
    }
}
  
public class Invoker
{
    public CommandBase Command { get; set; }

    public void ExecuteCommand()
    {
        Command.Execute();
    }
}

public abstract class CommandBase
{
    protected readonly Receiver _receiver;

    public CommandBase(Receiver receiver)
    {
        _receiver = receiver;
    }

    public abstract void Execute();
}

public class ConcreteCommand : CommandBase
{
    public string Parameter { get; set; }

    public ConcreteCommand(Receiver receiver) : base(receiver) { }

    public override void Execute()
    {
        _receiver.Action(Parameter);
    }
}

真实世界示例

我准备了一个简单的例子来演示命令模式的使用。在这个例子中,命令模式用于控制机器人的运动。在这个例子中,客户端是一个应用程序。命令的接收者是机器人本身。Robot类有四个控制运动的方法:MoveRotateLeftRotateRightTakeSampleRobotCommandBase是所有具体命令类的抽象基类。它有一个受保护的Robot字段,指向Robot对象,以及必须由具体命令覆盖的抽象方法MoveUndoRobotController类在这个例子中是调用者。它包含两个方法:ExecuteCommandsUndoCommands。第一个方法将执行队列中的所有命令,而UndoCommands方法是撤销功能,可以根据需要撤销任意数量的命令。

public abstract class RobotCommandBase
{
    protected Robot _robot;

    public RobotCommandBase(Robot robot)
    {
        _robot = robot;
    }

    public abstract void Execute();

    public abstract void Undo();
}

public class MoveCommand:RobotCommandBase
{
    public int ForwardDistance { get; set; }

    public MoveCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.Move(ForwardDistance);
    }

    public override void Undo()
    {
        _robot.Move(-ForwardDistance);
    }
}

public class RotateLeftCommand : RobotCommandBase
{
    public double LeftRotationAngle { get; set; }

    public RotateLeftCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.RotateLeft(LeftRotationAngle);
    }

    public override void Undo()
    {
        _robot.RotateRight(LeftRotationAngle);
    }
}

public class RotateRightCommand : RobotCommandBase
{
    public double RightRotationAngle { get; set; }

    public RotateRightCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.RotateRight(RightRotationAngle);
    }
 
    public override void Undo()
    {
        _robot.RotateLeft(RightRotationAngle);
    }
}

public class TakeSampleCommand : RobotCommandBase
{
    public bool TakeSample { get; set; }

    public TakeSampleCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.TakeSample(true);
    }

    public override void Undo()
    {
        _robot.TakeSample(false);
    }
}

public class RobotController
{
    public Queue<RobotCommandBase> Commands;
    private Stack<RobotCommandBase> _undoStack;

    public RobotController()
    {
        Commands = new Queue<RobotCommandBase>();
        _undoStack = new Stack<RobotCommandBase>();
    }

    public void ExecuteCommands()
    {
        Console.WriteLine("EXECUTING COMMANDS.");

        while (Commands.Count > 0)
        {
            RobotCommandBase command = Commands.Dequeue();
            command.Execute();
            _undoStack.Push(command);
        }
    }

    public void UndoCommands(int numUndos)
    {
        Console.WriteLine("REVERSING {0} COMMAND(S).", numUndos);

        while (numUndos > 0 && _undoStack.Count > 0)
        {
            RobotCommandBase command = _undoStack.Pop();
            command.Undo();
            numUndos--;
        }
    }
}

public class Robot
{
    public void Move(int distance)
    {
        if (distance > 0)
            Console.WriteLine("Robot moved forwards {0}mm.", distance);
        else
            Console.WriteLine("Robot moved backwards {0}mm.", -distance);
    }

    public void RotateLeft(double angle)
    {
        if (angle > 0)
            Console.WriteLine("Robot rotated left {0} degrees.", angle);
        else
            Console.WriteLine("Robot rotated right {0} degrees.", -angle);
    }

    public void RotateRight(double angle)
    {
        if (angle > 0)
            Console.WriteLine("Robot rotated right {0} degrees.", angle);
        else
            Console.WriteLine("Robot rotated left {0} degrees.", -angle);
    }

    public void TakeSample(bool take)
    {
        if(take)
            Console.WriteLine("Robot took sample");
        else
            Console.WriteLine("Robot released sample");
    }
}

class Program
{
    static void Main()
    {
        var robot = new Robot();
        var controller = new RobotController();

        var move = new MoveCommand(robot);
        move.ForwardDistance = 1000;
        controller.Commands.Enqueue(move);

        var rotate = new RotateLeftCommand(robot);
        rotate.LeftRotationAngle = 45;
        controller.Commands.Enqueue(rotate);

        var scoop = new TakeSampleCommand(robot);
        scoop.TakeSample = true;
        controller.Commands.Enqueue(scoop);

        controller.ExecuteCommands();
        controller.UndoCommands(3);
    }
}

解释器模式

解释器模式是一种设计模式,在开发领域特定语言或符号时很有用。该模式允许以面向对象的方式表示此类符号的语法,并且可以轻松扩展。

解释器模式是另一种行为设计模式,它指定了如何评估语言中的句子。这种模式以形式语法的形式描述。它根据一组表达式执行一些活动。我们有两种类型的表达式:终结符和非终结符。这些类型之间的区别非常简单。终结符表达式代表可以立即评估的结构,而非终结符表达式由一个或多个可以是终结符或非终结符的表达式组成。解释器模式与组合模式非常相似。终结符表达式代表这种树结构的叶子,而非终结符表达式代表复合体。解释器定义行为,而组合模式只定义结构。

结构代码示例

下面的UML图描述了解释器设计模式的一种实现。该图由五部分组成

  • 客户端:构建(或给定)一个抽象语法树,表示语法定义的语言中的特定句子。抽象语法树由NonterminalExpressionTerminalExpression类的实例组装而成,调用Interpret操作。
  • 上下文:包含对解释器全局的信息。
  • ExpressionBase:声明所有表达式的接口或抽象类。
  • TerminalExpresion:实现与语法中的终结符相关的Interpret操作。句子中的每个终结符都需要一个实例。
  • NonterminalExpression:非终结符表达式使用ExpressionBase的具体子类表示。这些表达式是包含一个或多个进一步表达式的聚合,每个表达式可以是终结符或非终结符。当非终结符表达式类的Interpret方法被调用时,解释过程包括调用它所持有的表达式的Interpret方法。
static class Program
{
    static void Main()
    {
        Client.BuildAndInterpretCommands();
    }
}

public static class Client
{
    public static void BuildAndInterpretCommands()
    {
        var context = new Context("hello world!!!");
        var root = new NonterminalExpression
                       {
                           Expression1 = new TerminalExpression(),
                           Expression2 = new TerminalExpression()
                       };
        root.Interpret(context);
    }
}

public class Context
{
    public string Name { get; set; }

    public Context(string name)
    {
        Name = name;
    }
}

public abstract class ExpressionBase
{
    public abstract void Interpret(Context context);
}
 
 
public class TerminalExpression : ExpressionBase
{
    public override void Interpret(Context context)
    {
        Console.WriteLine("Terminal for {0}.", context.Name);
    }
}


public class NonterminalExpression : ExpressionBase
{
    public ExpressionBase Expression1 { get; set; }

    public ExpressionBase Expression2 { get; set; }

    public override void Interpret(Context context)
    {
        Console.WriteLine("Nonterminal for {0}.", context.Name);
        Expression1.Interpret(context);
        Expression2.Interpret(context);
    }
}

真实世界示例

作为一个例子,我决定实现一个简化的波兰表示法求值器。这个求值器允许您添加或减去简单的整数值。波兰表示法的语法非常简单。整数值代表终结符,减法和加法运算符代表非终结符。在这个例子中,没有必要创建自定义上下文类,因为上下文由包含数字和+和-符号的字符串表示。在这个例子中,我们有一个IExpressionBase接口,它包含了Evaluate方法的签名,该方法评估表达式并返回评估结果。接下来我们有三个实现IExpressionBase接口的具体表达式类。IntegerExpression类代表基本整数。AdditionExpressionSubtractionExpression类是非终结符表达式类。这两个类都有一个带有两个IExpressionBase参数的构造函数。这两个参数都必须被评估,并且评估结果由AdditionExpressionSubtractionExpressionClasses返回。

    public interface IExpressionBase
    {
        int Evaluate();
    }
 
    public class AdditionExpression : ExpressionBase.IExpressionBase
    {
        ExpressionBase.IExpressionBase _expr1;
        ExpressionBase.IExpressionBase _expr2;
 
        public AdditionExpression(ExpressionBase.IExpressionBase expr1, ExpressionBase.IExpressionBase expr2)
        {
            _expr1 = expr1;
            _expr2 = expr2;
        }
 
        public int Evaluate()
        {
            int value1 = _expr1.Evaluate();
            int value2 = _expr2.Evaluate();
            return value1 + value2;
        }
 
        public override string ToString()
        {
            return string.Format("({0} + {1})", _expr1, _expr2);
        }
    }
 
    public class SubtractionExpression : ExpressionBase.IExpressionBase
    {
        ExpressionBase.IExpressionBase _expr1;
        ExpressionBase.IExpressionBase _expr2;
 
        public SubtractionExpression(ExpressionBase.IExpressionBase expr1, 
               ExpressionBase.IExpressionBase expr2)
        {
            _expr1 = expr1;
            _expr2 = expr2;
        }
 
        public int Evaluate()
        {
            int value1 = _expr1.Evaluate();
            int value2 = _expr2.Evaluate();
            return value1 - value2;
        }
 
        public override string ToString()
        {
            return string.Format("({0} - {1})", _expr1, _expr2);
        }
    }
 
    public class IntegerExpression:ExpressionBase.IExpressionBase
    {
        int _value;
 
        public IntegerExpression(int value)
        {
            _value = value;
        }
 
        public int Evaluate()
        {
            return _value;
        }
 
        public override string ToString()
        {
            return _value.ToString();
        }
    }
 
    static void Main(string[] args)
    {
        var parser = new Parser();

        var commands = new string[]
                                {
                                    "+ 5 6",
                                    "- 6 5",
                                    "+ - 4 5 6",
                                    "+ 4 - 5 6",
                                    "+ - + - - 2 3 4 + - -5 6 + -7 8 9 10"
                                };

        foreach (var command in commands)
        {
            ExpressionBase.IExpressionBase expression = parser.Parse(command);
            Console.WriteLine("{0} = {1}", expression, expression.Evaluate());
        }
    }
}

public class Parser
{
    public ExpressionBase.IExpressionBase Parse(string polish)
    {
        var symbols = new List<string>(polish.Split(' '));
        return ParseNextExpression(symbols);
    }

    public ExpressionBase.IExpressionBase ParseNextExpression(List<string> symbols)
    {
        int value;
        if (int.TryParse(symbols[0], out value))
        {
            symbols.RemoveAt(0);
            return new IntegerExpression(value);
        }
        return ParseNonTerminalExpression(symbols);
    }

    private ExpressionBase.IExpressionBase ParseNonTerminalExpression(List<string> symbols)
    {
        var symbol = symbols[0];
        symbols.RemoveAt(0);

        var expr1 = ParseNextExpression(symbols);
        var expr2 = ParseNextExpression(symbols);

        switch (symbol)
        {
            case "+":
                return new AdditionExpression(expr1, expr2);
            case "-":
                return new SubtractionExpression(expr1, expr2);
            default:
                string message = string.Format("Invalid Symbol ({0})", symbol);
                throw new InvalidOperationException(message);
        }
    }
}

此示例取自www.blackwasp.co.uk/Interpreter.aspx

迭代器模式

迭代器模式是一种设计模式,它提供了一种顺序访问聚合对象元素的方法,而无需了解其结构。这允许以标准方式遍历列表、树和其他结构。

软件开发中最常见的数据结构之一是通常称为集合的结构。集合是一组对象。这些对象可以具有相同的类型,也可以都转换为基类型。集合可以是一个数组、列表等等。关于集合最重要的事情之一是它能够在不暴露集合内部结构的情况下访问元素。迭代器模式的主要目的是为遍历项目集合提供一个简单的接口。迭代器负责访问和遍历集合中的对象,并将其放入迭代器对象中。迭代器对象将维护迭代的状态,跟踪当前项目并提供一种识别下一个要迭代的元素的方法。

结构代码示例

下面的UML图描述了解释器设计模式的一种实现。该图由五部分组成

  • 客户端:当该对象想要遍历项目时,它从聚合对象请求并使用迭代器。
  • AggregateBase:这是所有具体聚合的抽象基类(或接口)。该类包含一个返回迭代器的方法。
  • ConcreteAggregate:表示AggregateBase的具体实现。它持有可以使用迭代器遍历的项目。
  • IteratorBase:这是所有具体迭代器的抽象类(或接口)。它包含允许您遍历项目的方法(如果是接口,则是方法的签名)。
  • ConcreteIterator:这是IteratorBase的具体实现。
static class Program
{
    static void Main()
    {
        var aggregate = new ConcreteAggregate();
        aggregate[0] = "Item 1";
        aggregate[1] = "Item 2";
        aggregate[2] = "Item 3";
        aggregate[3] = "Item 4";


        var itorator = new ConcreteIterator(aggregate);


        object item = itorator.First();
        while (item != null)
        {
            Console.WriteLine(item);
            item = itorator.Next();
        }
    }
}
abstract class Iterator
{
    public abstract object First();
    public abstract object Next();
    public abstract bool IsDone();
    public abstract object CurrentItem();
}

abstract class Aggregate
{
    public abstract Iterator CreateIterator();
}

class ConcreteIterator : Iterator
{
    private readonly ConcreteAggregate _aggregate;
    private int _current;

    public ConcreteIterator(ConcreteAggregate aggregate)
    {
        _aggregate = aggregate;
    }

    public override object First()
    {
        return _aggregate[0];
    }

    public override object Next()
    {
        object ret = null;
        if (_current < _aggregate.Count - 1)
        {
            ret = _aggregate[++_current];
        }

        return ret;
    }

    public override object CurrentItem()
    {
        return _aggregate[_current];
    }

    public override bool IsDone()
    {
        return _current >= _aggregate.Count;
    }
}
 
class ConcreteAggregate : Aggregate
{
    private readonly ArrayList _items = new ArrayList();

    public override Iterator CreateIterator()
    {
        return new ConcreteIterator(this);
    }

    public int Count
    {
        get { return _items.Count; }
    }

    public object this[int index]
    {
        get { return _items[index]; }
        set { _items.Insert(index, value); }
    }
}

真实世界示例

在这个例子中,我决定创建一个自定义的Person对象集合(PersonAggregate)。这个自定义集合是一个聚合,它实现了IPersonEnumerable聚合基接口。该接口包含GetIterator()方法的签名,该方法返回此集合的迭代器。接下来我创建了PersonEnumerator类,它是IPersonIterator接口的具体实现。这个类有一个接受IPersonEnumerable对象的构造函数。PersonEnumerator类有三个方法,允许您遍历人员列表。MoveFirst方法将游标设置在列表的第一个元素。MoveNext方法将游标设置在列表的下一个元素,并且如果当前项目之后集合中存在另一个元素,则返回true。最后一个方法Reset将游标设置在列表的第一个元素之前。

public interface IPersonEnumerable
{
    IPersonIterator GetIterator();
}

public class PersonAggregate : IPersonEnumerable
{
    private List<Person> _persons = new List<Person>();

    public Person this[int index]
    {
        get { return _persons[index]; }
    }

    public IPersonIterator GetIterator()
    {
        return new PersonEnumerator(this);
    }

    public int Count
    {
        get { return _persons.Count; }
    }

    public void Add(Person person)
    {
        _persons.Add(person);
    }
}

public class PersonEnumerator:IPersonIterator
{
    private PersonAggregate _aggregate;
    int _position;
 
    public PersonEnumerator(PersonAggregate aggregate)
    {
        _aggregate = aggregate;
    }

    public void MoveFirst()
    {
        if(_aggregate.Count==0)
        {
            throw new InvalidOperationException();
        }
        _position = 0;
    }

    public void Reset()
    {
        _position = -1;
    }

    public bool MoveNext()
    {
        _position++;
        return _position < _aggregate.Count;
    }

    public Person Current
    {
        get
        {
            if (_position < _aggregate.Count)
                return _aggregate[_position];
            throw new InvalidOperationException();
        }
    }
}

public interface IPersonIterator
{
    void MoveFirst();
    void Reset();
    bool MoveNext();
    Person Current { get; }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public double Height { get; set; }
}
 
static void Main(string[] args)
{
    PersonAggregate aggregate = new PersonAggregate();
    aggregate.Add(new Person(){FirstName = "Robert", Height = 168,LastName = "Kanasz"});
    aggregate.Add(new Person(){FirstName = "John", Height = 181, LastName = "Doe" });
    aggregate.Add(new Person() { FirstName = "Jane", Height = 158, LastName = "Doe" });

    IPersonIterator iterator = aggregate.GetIterator();

    iterator.Reset();

    while (iterator.MoveNext())
    {
        Console.WriteLine(iterator.Current.FirstName + " " + iterator.Current.LastName);
    }
}

中介者模式

中介者模式是一种设计模式,通过消除类之间直接通信的需要来促进对象的松散耦合。相反,中介者对象用于封装和集中类之间的交互。

中介者模式是另一种行为设计模式。在某些情况下,程序由大型类组成。这并不罕见,但是当这些类需要相互通信时就会出现问题。当类直接通信时,使用传统方法,这些类需要了解其内部实现。这可能会导致一个非常大的混乱,因为当程序变得越来越复杂时,它变得更难阅读和理解。这些类是紧密耦合的,从设计的角度来看这不是很好,将来可能会导致问题。

中介者设计模式解决了这个问题。在这种模式中,对象之间的通信被封装在一个中介者对象中。类不是直接通信,而是将消息发送给中介者,中介者将这些消息发送给其他类。

结构代码示例

下面的UML图描述了中介者设计模式的实现。该图由四部分组成

  • ColleagueBase:这是所有具体同事的抽象类(或接口)。它有一个受保护的字段,持有对中介者对象的引用。
  • ConcreteColleague:这是ColleagueBase类的实现。具体同事是相互通信的类。
  • MediatorBase:这是中介者对象的抽象类。该类包含可以被同事使用的各种方法。
  • ConcreateMediator:该类实现了MediatorBase类的通信方法。该类持有所有需要通信的同事的引用。
static class Program
{
    static void Main()
    {
        var m = new ConcreteMediator();

        var c1 = new ConcreteColleague1(m);
        var c2 = new ConcreteColleague2(m);

        m.Colleague1 = c1;
        m.Colleague2 = c2;

        c1.Send("How are you?");
        c2.Send("Fine, thanks");
    }
}
public abstract class ColleagueBase
{
    protected MediatorBase _mediator;

    public ColleagueBase(MediatorBase mediator)
    {
        _mediator = mediator;
    }
}

public abstract class MediatorBase
{
    public abstract void SendMessage(ColleagueBase caller, string message);
}

class ConcreteMediator : MediatorBase
{
    private ConcreteColleague1 _colleague1;
    private ConcreteColleague2 _colleague2;

    public ConcreteColleague1 Colleague1
    {
        set { _colleague1 = value; }
    }

    public ConcreteColleague2 Colleague2
    {
        set { _colleague2 = value; }
    }

   
    public override void SendMessage(ColleagueBase caller, string message)
    {
        if (caller == _colleague1)
        {
            _colleague2.Notify(message);
        }
        else
        {
            _colleague1.Notify(message);
        }
    }
}
 
class ConcreteColleague1 : ColleagueBase
{
    public ConcreteColleague1(MediatorBase mediator)
        : base(mediator)
    {
    }

    public void Send(string message)
    {
        _mediator.SendMessage(this,message);
    }

    public void Notify(string message)
    {
        Console.WriteLine("Colleague1 gets message: " + message);
    }
}

class ConcreteColleague2 : ColleagueBase
{
    public ConcreteColleague2(MediatorBase mediator)
        : base(mediator)
    {
    }

    public void Send(string message)
    {
        _mediator.SendMessage(this, message);
    }

    public void Notify(string message)
    {
        Console.WriteLine("Colleague2 gets message: "+ message);
    }
}

真实世界示例

在现实世界中实现这个例子真的很简单。假设您需要创建一个需要控制多架飞机的应用程序。与其让飞机之间进行通信,不如实现一个集中式机制,该机制接收来自所有飞机的消息,处理它们,然后将它们发送给其他飞机。

在这个例子中,有几架飞机由类表示:Airbus380Boeing747LearJet45。这些类继承自Aircraft抽象基类并充当同事。RegionalAirTrafficControll类是中介者类的具体实现,并实现了IAirTrafficControl接口(MediatorBase)。RegionalAirTraffic控制对象持有所有需要相互通信的飞机的引用。

每当飞机改变飞行高度时,它都会向中介者(RegionalAirTraffincControl)发送消息。中介者会查找是否有任何飞机与消息发送者处于相同的飞行走廊中,如果存在,它会向该飞机发送通知消息,并命令发送者增加其高度。

public abstract class Aircraft
{
    private readonly IAirTrafficControl _atc;
    private int _altitude;

    public string RegistrationNumber { get; set; }

    public int Altitude
    {
        get { return _altitude; }
        set
        {
            _altitude = value;
            _atc.SendWarningMessage(this);
        }
    }
 
    public Aircraft(string registrationNumber, IAirTrafficControl atc)
    {

        RegistrationNumber = registrationNumber;
        _atc = atc;
        _atc.RegistrerAircraft(this);
    }

    public void Climb(int heightToClimb)
    {
        Altitude += heightToClimb;
    }

    public void ReceiveWarning(Aircraft reportingAircraft)
    {
        Console.WriteLine("ATC: ({0}) - {1} is at your flight altitude!!!",
          this.RegistrationNumber,reportingAircraft.RegistrationNumber);
    }
}

public class Airbus380:Aircraft
{
    public Airbus380(string registrationNumber, IAirTrafficControl atc) : base(registrationNumber, atc)
    {
    }
}

public class Boeing747: Aircraft
{
    public Boeing747(string registrationNumber, IAirTrafficControl atc) : base(registrationNumber, atc)
    {
    }
}

public class LearJet45:Aircraft
{
    public LearJet45(string registrationNumber, IAirTrafficControl atc) : base(registrationNumber, atc)
    {
    }
}

public interface IAirTrafficControl
{
    void RegistrerAircraft(Aircraft aircraft);

    void SendWarningMessage(Aircraft aircraft);
}

public class RegionalAirTrafficControl : IAirTrafficControl
{
    readonly List<Aircraft> _registeredAircrafts = new List<Aircraft>();

    public void RegistrerAircraft(Aircraft aircraft)
    {
        if (!_registeredAircrafts.Contains(aircraft))
        {
            _registeredAircrafts.Add(aircraft);
        }
    }

    public void SendWarningMessage(Aircraft aircraft)
    {
        var list = from a in _registeredAircrafts
                   where a != aircraft &&
                         Math.Abs(a.Altitude - aircraft.Altitude) < 1000
                   select a;
        foreach (var a in list)
        {
            a.ReceiveWarning(aircraft);
            aircraft.Climb(1000);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var regionalATC = new RegionalAirTrafficControl();
        var aircraft1 = new Airbus380("AI568", regionalATC);
        var aircraft2 = new Boeing747("BA157", regionalATC);
        var aircraft3 = new Airbus380("LW111", regionalATC);

        aircraft1.Altitude += 100;

        aircraft3.Altitude = 1100;
    }
}

备忘录模式

备忘录模式是一种设计模式,它允许在不破坏封装规则的情况下存储对象的当前状态。原始对象可以根据需要进行修改,但可以随时恢复到保存的状态。

有时有必要在某个时间点捕获对象的内部状态,并能够在运行时稍后恢复该状态。这是您可以使用备忘录模式的正确地方。这种模式用于为应用程序提供撤销功能。有时它被称为带回滚的撤销。

结构代码示例

下面的UML图描述了备忘录设计模式的一种实现。该图由三部分组成

  • 发起人:创建一个备忘录对象,捕获发起人的内部状态。发起人使用备忘录对象来恢复其之前的状态。
  • 备忘录:存储发起人对象的内部状态。状态可以包括任意数量的状态变量。备忘录必须有两个接口。一个面向看管者的接口:此接口绝不允许对备忘录存储的内部状态进行任何操作或访问,从而遵守封装。另一个接口面向发起人,允许发起人访问发起人恢复先前状态所需的任何状态变量。
  • 看管者:负责保管备忘录。备忘录对看管者来说是不透明的,看管者不得对其进行操作。
static class Program
{
    static void Main()
    {
        var originator = new Originator {State = "State A"};
        Console.WriteLine(originator.State);
        var caretaker = new Caretaker {Memento = originator.CreateMemento()};
        originator.State = "State B";
        Console.WriteLine(originator.State);
        originator.SetMemento(caretaker.Memento);
        Console.WriteLine(originator.State);
    }
}

class Originator
{
    private string _state;

    public string State
    {
        get { return _state; }
        set
        {
            _state = value;
        }
    }

    public Memento CreateMemento()
    {
        return (new Memento(_state));
    }

    public void SetMemento(Memento memento)
    {
        State = memento.GetState();
    }
}

class Memento
{
    private readonly string _state;

    public Memento(string state)
    {
        _state = state;
    }

    public string GetState()
    {
        return _state;

    }
}

class Caretaker
{
    private Memento _memento;

    public Memento Memento
    {
        set { _memento = value; }
        get { return _memento; }
    }
}

真实世界示例

在这个例子中,我们想要在Person类的某个时间点跟踪状态。某个时间点的状态存储在备忘录类PersonMemento中,该类具有与Person类相同的属性(或状态变量),但这里有一个小小的不同。备忘录类的所有状态变量都是通过构造函数设置的,一旦它们被填充,我们就无法更改它们。当创建备忘录时,它使用Add方法存储在看管者对象的内部堆栈中。Caretaker有另一个方法调用GetMemento,它从内部堆栈的顶部移除并返回对象。

public class PersonCaretaker
{
    readonly Stack<PersonMemento> _mementos = new Stack<PersonMemento>();

    public PersonMemento GetMemento()
    {
        return _mementos.Pop();
    }

    public void Add(PersonMemento memento)
    {
        _mementos.Push(memento);
    }
}

public class PersonMemento
{
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public string CellPhone { get; private set; }
    public string Address { get; private set; }

    public PersonMemento(string fName, string lName, string cell, string address)
    {
        FirstName = fName;
        LastName = lName;
        CellPhone = cell;
        Address = address;
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string CellPhone { get; set; }
    public string Address { get; set; }

    public PersonMemento CreateUnDo()
    {
        return new PersonMemento(FirstName,LastName,CellPhone,Address);
    }

    public  void UnDo(PersonMemento memento)
    {
        FirstName = memento.FirstName;
        LastName = memento.LastName;
        CellPhone = memento.CellPhone;
        Address = memento.Address;
    }

    public void ShowInfo()
    {
        Console.WriteLine("FIRST NAME: {0}",FirstName);
        Console.WriteLine("LAST NAME: {0}", LastName);
        Console.WriteLine("ADDRESS: {0}", Address);
        Console.WriteLine("CELLPHONE: {0}", CellPhone);
    }
}

class Program
{
    static void Main()
    {
        var person = new Person
                         {
                                Address = "Under the Bridge 171",
                                CellPhone = "122011233185",
                                FirstName = "John",
                                LastName = "Doe"
                            };

        var caretaker = new PersonCaretaker();
        caretaker.Add(person.CreateUnDo());

        person.ShowInfo();
        Console.WriteLine();

        person.Address = "Under the Tree 119";
        caretaker.Add(person.CreateUnDo());

        person.ShowInfo();
        Console.WriteLine();

        person.CellPhone = "987654321";
        person.ShowInfo();
        Console.WriteLine();

        person.UnDo(caretaker.GetMemento());
        person.ShowInfo();
        Console.WriteLine();

        person.UnDo(caretaker.GetMemento());
        person.ShowInfo();
        Console.WriteLine();
    }
}

观察者模式

观察者模式是一种设计模式,它定义了对象之间的链接,以便当一个对象的状态改变时,所有依赖对象都会自动更新。这种模式允许对象之间以松散耦合的方式进行通信。

使用这种设计模式,您可以监控和发布单个对象(称为主题)的变化。当发生变化时,其他对象(称为观察者)可以通过调用其方法之一而自动收到通知。这种模式在主题和观察者之间提供了松散耦合。主题持有所有观察者的引用,当发生变化时必须通知这些观察者。

结构代码示例

下面的UML图描述了观察者设计模式的一种实现。该图由四部分组成

  • SubjectBase:这是所有主题的基类。它包含一个受保护的订阅该主题的观察者列表,以及允许添加或删除观察者的方法。它还包含一个Notify方法,该方法遍历观察者列表并调用它们的Notify方法。
  • ConcreteSubject:这是SubjectBase类的具体实现。它维护自己的状态,当发生变化时,它会调用Notify方法,该方法遍历所有观察者并调用它们的Notify方法。
  • ObserverBase:这是所有观察者的抽象类。它包含当主题状态改变时被调用的方法。
  • ConcreteObserver:这是ObserverBase的具体实现。
static class Program
{
    static void Main()
    {
        var subject = new ConcreteSubject();
        subject.Attach(new ConcreteObserver(subject,"Observer 1"));
        subject.Attach(new ConcreteObserver(subject,"Observer 2"));
        subject.Attach(new ConcreteObserver(subject,"Observer 3"));
        subject.SetState("STATE");
    }
}

public abstract class SubjectBase
{
    private ArrayList _observers = new ArrayList();

    public void Attach(ObserverBase o)
    {
        _observers.Add(o);
    }

    public void Detach(ObserverBase o)
    {
        _observers.Remove(o);
    }

    public void Notify()
    {
        foreach (ObserverBase o in _observers)
        {
            o.Update();
        }
    }
}

public class ConcreteSubject : SubjectBase
{
    private string _state;

    public string GetState()
    {
        return _state;
    }
 
    public void SetState(string newState)
    {
        _state = newState;
        Notify();
    }
}

public abstract class ObserverBase
{
    public abstract void Update();
}


public class ConcreteObserver : ObserverBase
{
    private ConcreteSubject _subject;
    private string _name;

    public ConcreteObserver(ConcreteSubject subject, string name)
    {
        _subject = subject;
        _name = name;
    }

    public override void Update()
    {
        string subjectState = _subject.GetState();
        Console.WriteLine(_name + ": " +subjectState);
    }
}

真实世界示例

假设我们有一个股票系统,它提供来自多家公司的数据。在这个例子中,我们有两个观察者:AllStockObserverIBMStockObserverAllStockObserver响应ConcreteStockTicker类的所有变化。IBMStockTicker类只在IBM公司的股票从ConcreteStockTicker中出来时响应。

public class AllStockObserver : IStockObserverBase
{
    public AllStockObserver(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void Notify(Stock stock)
    {
        Console.WriteLine("Notified {0} of {1}'s " +
                          "change to {2:C}", Name, stock.Code, stock.Price);
    }
}

public class IBMStockObserver:IStockObserverBase
{
    public IBMStockObserver(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void Notify(Stock stock)
    {
        if(stock.Code=="IBM")
            Console.WriteLine("Notified {0} of {1}'s " +
                          "change to {2:C}", Name, stock.Code, stock.Price);
    }
}
 
public class ConcreteStockTicker:StockTickerBase
{
    private Stock stock;
    public Stock Stock
    {
        get { return stock; }
        set { 
            stock = value;
            Notify(); 
        }
    }

    public override void Notify()
    {
        foreach (var observer in _observers)
        {
            observer.Notify(stock);
        }
    }
}

public class Stock
{
    public decimal Price { get; set; }
    public string Code { get; set; }
}

public interface IStockObserverBase
{
    void Notify(Stock stock);
}
 
public abstract class StockTickerBase
{
    readonly protected List<IStockObserverBase> _observers = new List<IStockObserverBase>();

    public void Register(IStockObserverBase observer)
    {
        if(!_observers.Contains(observer))
        {
            _observers.Add(observer);
        }
    }

    public void Unregister(IStockObserverBase observer)
    {
        if (_observers.Contains(observer))
        {
            _observers.Remove(observer);
        }
    }

    public abstract void Notify();
}

class Program
{
    static void Main(string[] args)
    {
        var stockTicker = new ConcreteStockTicker();
        var ibmObserver = new IBMStockObserver("ROBER KANASZ");
        var allObserver = new AllStockObserver("IVOR LOTOCASH");

        stockTicker.Register(ibmObserver);
        stockTicker.Register(allObserver);

        foreach (var s in StockData.getNext())
            stockTicker.Stock = s;
    }
}

public static class StockData
{
    private static decimal[] samplePrices = 
       new decimal[] { 10.00m, 10.25m, 555.55m, 9.50m, 9.03m, 500.00m, 499.99m, 10.10m, 10.01m };
    private static string[] sampleStocks = new string[] { "MSFT", "MSFT", 
      "GOOG", "MSFT", "MSFT", "GOOG", 
      "GOOG", "MSFT", "IBM" };
    public static IEnumerable<Stock> getNext()
    {
        for (int i = 0; i < samplePrices.Length; i++)
        {
            Stock s = new Stock();
            s.Code = sampleStocks[i];
            s.Price = samplePrices[i];
            yield return s;
        }
    }
}

状态模式

状态模式是一种设计模式,它允许对象根据其当前的内部状态完全改变其行为。通过在定义的上下文中替换类,状态对象在运行时似乎改变了其类型。

这种设计模式允许您根据对象内部状态的变化来改变其行为。它允许在运行时改变行为,而无需改变用于访问对象的接口。这种改变隐藏在该对象的上下文中。这种模式在创建软件状态机时非常有用,其中对象的S功能根据其状态发生根本性变化。

结构代码示例

下面的UML图描述了状态设计模式的一种实现。该图由三部分组成

  • 上下文:该类持有具体的状态对象,该对象根据其当前状态提供行为。
  • StateBase:这是所有具体状态类的抽象类。它定义了上下文类使用的接口。
  • ConcreteState:这是StateBase类的具体实现。它提供了上下文类使用的实际功能。
static class Program
{
    static void Main()
    {
        var context = new Context(new ConcreteStateA());
        context.Request();
        context.Request();
        context.Request();
        context.Request();
    }
}

public class Context
{
    private StateBase _state;

    public Context(StateBase state)
    {
        _state = state;
        State = _state;
    }

    public void Request()
    {
        _state.Handle(this);
    }

    public StateBase State
    {
        set
        {
            _state = value;
            Console.WriteLine("Current state: {0}",_state.GetType());
        }
    }
}

public abstract class StateBase
{
    public abstract void Handle(Context context);
}

class ConcreteStateA : StateBase
{
    public override void Handle(Context context)
    {
        context.State = new ConcreteStateB();
    }
}

class ConcreteStateB : StateBase
{
    public override void Handle(Context context)
    {
        context.State = new ConcreteStateA();
    }
}

真实世界示例

DVD播放器被选作一个真实世界的例子,我希望在此基础上演示状态设计模式。假设DVD播放器有两个按钮:播放和菜单。当您根据其选定的状态按下这些按钮时,DVD播放器将表现出不同的行为。下表描述了DVD播放器根据其状态的行为

状态 播放按钮 菜单按钮
MoviePlayingState(电影播放状态) 停止播放。状态切换到MoviePausedState(电影暂停状态)。 状态切换到MenuState(菜单状态)。
MoviePausedState(电影暂停状态) 开始播放。状态切换到MoviePlayingState(电影播放状态)。 状态切换到MenuState(菜单状态)。
StandByState(待机状态) 开始播放。状态切换到MoviePlayingState(电影播放状态)。 状态切换到MenuState(菜单状态)。
MenuState(菜单状态) 无状态变化。 状态切换到StandByState(待机状态)。
public class MenuState:DVDPlayerState
{
    public MenuState()
    {
        Console.WriteLine("MENU");
    }

    public override void PlayButtonPressed(DVDPlayer player)
    {
        Console.WriteLine("  Next Menu Item Selected");
    }

    public override void MenuButtonPressed(DVDPlayer player)
    {
        player.State = new StandbyState();
    }
}

public class MoviePausedState:DVDPlayerState
{
    public MoviePausedState()
    {
        Console.WriteLine("MOVIE PAUSED");
    }

    public override void PlayButtonPressed(DVDPlayer player)
    {
        player.State = new MoviePlayingState();
    }

    public override void MenuButtonPressed(DVDPlayer player)
    {
        player.State = new MenuState();
    }
}

public class MoviePlayingState:DVDPlayerState
{
    public MoviePlayingState()
    {
        Console.WriteLine("MOVIE PLAYING");
    }
 
    public override void PlayButtonPressed(DVDPlayer player)
    {
        player.State = new MoviePausedState();
    }

    public override void MenuButtonPressed(DVDPlayer player)
    {
        player.State = new MenuState();
    }
}

public class StandbyState:DVDPlayerState
{
    public StandbyState()
    {
        Console.WriteLine("STANDBY");
    }

    public override void PlayButtonPressed(DVDPlayer player)
    {
        player.State = new MoviePlayingState();
    }

    public override void MenuButtonPressed(DVDPlayer player)
    {
        player.State = new MenuState();
    }
}
 
public class DVDPlayer
{
    private DVDPlayerState _state;

    public DVDPlayer()
    {
        _state = new StandbyState();
    }

    public DVDPlayer(DVDPlayerState state)
    {
        _state = state;
    }

    public void PressPlayButton()
    {
        _state.PlayButtonPressed(this);
    }

    public void PressMenuButton()
    {
        _state.MenuButtonPressed(this);
    }

    public DVDPlayerState State
    {
        get { return _state; }
        set { _state = value; }
    }
}

public abstract class DVDPlayerState
{
public abstract void PlayButtonPressed(DVDPlayer player);

public abstract void MenuButtonPressed(DVDPlayer player);
}

static void Main()
{
    var player = new DVDPlayer();

    player.PressPlayButton();
    player.PressMenuButton();
    player.PressPlayButton();
    player.PressPlayButton();
    player.PressMenuButton();
    player.PressPlayButton();
    player.PressPlayButton();
}

策略模式

策略模式是一种设计模式,它允许定义一组相似的算法,并将它们封装在自己的类中。然后可以根据您的要求在运行时选择用于特定目的的算法。

这种设计模式可用于类仅在行为上不同的常见情况。在这种情况下,将这些行为算法分离到可以在运行时选择的单独类中是一个好主意。这种模式定义了一系列算法,封装了每一个算法,并使它们可互换。这种分离允许行为独立于使用它的客户端而变化。它还增加了应用程序的灵活性,允许将来添加新算法。

结构代码示例

下面的UML图描述了策略设计模式的一种实现。该图由三部分组成

  • 客户端:该类使用可互换的算法。它维护对StrategyBase对象的引用。有时它定义了一个接口,允许StrategyBase访问其数据。
  • StrategyBase:声明所有支持算法通用的接口。客户端使用此接口调用由ConcreteStrategy定义的算法。
  • ConcreteStrategy:这是继承自StrategyBase类的具体策略类。每个实例都提供客户端可能使用的不同算法。
static class Program
{
    static void Main()
    {
        var client = new Client {Strategy = new ConcreteStrategyA()};
        client.CallAlgorithm();
        client.Strategy = new ConcreteStrategyB();
        client.CallAlgorithm();
    }
}

public class Client
{
    public StrategyBase Strategy { private get; set; }

    public void CallAlgorithm()
    {
        Strategy.Algorithm();
    }
}

public abstract class StrategyBase
{
    public abstract void Algorithm();
}

public class ConcreteStrategyA : StrategyBase
{
    public override void Algorithm()
    {
        Console.WriteLine("Concrete Strategy A");
    }
}

public class ConcreteStrategyB : StrategyBase
{
    public override void Algorithm()
    {
        Console.WriteLine("Concrete Strategy B");
    }
}

真实世界示例

在下面的示例中,我将向您展示如何使用策略设计模式。这个示例应用程序将模拟不同的机器人行为。在这个例子中,我创建了几个具有不同行为的Robot类实例。我创建了四个Robot类的实例。每个实例都通过构造函数传递了一个行为算法。我们有四种行为算法类,它们实现了IRobotBehaviour接口。

public class AggresiveBehaviour:IRobotBehaviour
{
    public void Move()
    {
        Console.WriteLine("\tAggresive Behaviour: if find another robot, attack it");
    }
}

public class BorgBehaviour:IRobotBehaviour
{
    public void Move()
    {
        Console.WriteLine("\tBorg Behaviour: if find another robot, assimilate it");
    }
}

public class DefensiveBehaviour:IRobotBehaviour
{
    public void Move()
    {
        Console.WriteLine("\tDefensive Behaviour: if find another robot, run from it");
    }
}

public class NormalBehaviour:IRobotBehaviour
{
    public void Move()
    {
        Console.WriteLine("\tNormal Behaviour: if find another robot, ignore it");
    }
}
 
public interface IRobotBehaviour
{
    void Move();
}

public class Robot
{
    private readonly IRobotBehaviour _behaviour;
    private readonly string _name;
    public Robot(string name, IRobotBehaviour behaviour)
    {
        _behaviour = behaviour;
        _name = name;
    }

    public void Move()
    {
        Console.WriteLine(_name);
        _behaviour.Move();
    }
}

class Program
{
    static void Main()
    {
        var robot1 = new Robot("4of11", new BorgBehaviour());
        var robor2 = new Robot("Military Robot", new AggresiveBehaviour());
        var robot3 = new Robot("rotoROBOt", new NormalBehaviour());

        robot1.Move();
        robor2.Move();
        robot3.Move();
    }
}

模板方法模式

模板方法模式是一种设计模式,它允许定义一组可互换的、结构相似的、多步骤算法。每个算法都遵循相同的操作序列,但提供不同的步骤实现。

这种设计模式允许您在运行时改变类的行为。它几乎与策略设计模式相同,但有一个显著的区别。策略设计模式覆盖整个算法,而模板方法允许您只改变行为算法的某些部分,使用抽象操作(整个算法被分成几个操作)。

结构代码示例

下面的UML图描述了模板方法设计模式的一种实现。该图由两部分组成

  • AlgorithmBase:定义抽象的原始操作,具体子类定义这些操作以实现算法的步骤。实现定义算法骨架的模板方法。模板方法调用原始操作以及在AbstractClass中定义的操作或其他对象的。
  • ConcreteAlgorithm:实现原始操作以执行算法的特定于子类的步骤。当调用具体类时,将从基类执行模板方法代码,而对于模板方法内部使用的每个方法,将调用派生类中的实现。
static class Program
{
    static void Main()
    {
        AlgorithmBase algorithmBase1 = new ConcreteAlgorithmA();
        algorithmBase1.TemplateMethod();

        AlgorithmBase algorithmBase2 = new ConcreteAlgorithmB();
        algorithmBase2.TemplateMethod();
    }
}

public abstract class AlgorithmBase
{
    public void TemplateMethod()
    {
        Step1();
        Step2();
        Step3();
    }

    protected abstract void Step1();

    protected abstract void Step2();

    protected abstract void Step3();
}

public class ConcreteAlgorithmA : AlgorithmBase
{
    protected override void Step1()
    {
        Console.WriteLine("ConcreteAlgorithmA.Step1()");
    }

    protected override void Step2()
    {
        Console.WriteLine("ConcreteAlgorithmA.Step2()");
    }

    protected override void Step3()
    {
        Console.WriteLine("ConcreteAlgorithmA.Step3()");
    }
}


public class ConcreteAlgorithmB : AlgorithmBase
{
    protected override void Step1()
    {
        Console.WriteLine("ConcreteAlgorithmB.Step1()");
    }

    protected override void Step2()
    {
        Console.WriteLine("ConcreteAlgorithmB.Step2()");
    }

    protected override void Step3()
    {
        Console.WriteLine("ConcreteAlgorithmB.Step3()");
    }
}

真实世界示例

这个例子是一个小的通知应用程序,它使用不同类型的通知发送者。发送者的消费者是Message类。每个发送者都继承自NotificationSenderBase类,该类有一个抽象方法Notify()

public abstract class NotificationSenderBase
{
    protected SystemOperator _operator;

    public NotificationSenderBase(SystemOperator systemOperator)
    {
        _operator = systemOperator;
    }

    protected abstract string GetNotificationMessageText();
    

    public abstract void Notify();

}

public class MailNotificationSender : NotificationSenderBase
{
    public MailNotificationSender(SystemOperator systemOperator) : base(systemOperator)
    {
    }

    protected override string GetNotificationMessageText()
    {
        return "[UNSPECIFIED ERROR OCCURED]";
    }

    public override void Notify()
    {
        Console.WriteLine("EMAIL MESSAGE:{0} WAS SET TO: {1}", GetNotificationMessageText(), _operator.Email);
    }
}

public class Message
{
    public NotificationSenderBase Sender { get; set; }

    public void Send()
    {
        Sender.Notify();
    }
}

public class SmsNotificationSender: NotificationSenderBase
{
    public SmsNotificationSender(SystemOperator systemOperator) : base(systemOperator)
    {
    }

    protected override string GetNotificationMessageText()
    {
        return "UNSPECIFIED ERROR OCCURED";
    }

    public override void Notify()
    {
        Console.WriteLine("SMS MESSAGE:{0} WAS SET TO: {1}", 
                GetNotificationMessageText(),_operator.CellPhone);
    }
}
 
public class SystemOperator
{
    public string Name { get; set; }
    public string Pager { get; set; }
    public string CellPhone { get; set; }
    public string Email { get; set; }
}

class Program
{
    static void Main()
    {

        var systemOperator = new SystemOperator
                                            {
                                                CellPhone = "145616456",
                                                Email = "system@operator.com",
                                                Name = "Super Operator",
                                                Pager = "465565456"
                                            };
        var message = new Message();

        message.Sender = new SmsNotificationSender(systemOperator);
        message.Send();

        message.Sender = new MailNotificationSender(systemOperator);
        message.Send();
    }
}

访问者模式

访问者模式是一种设计模式,它将一组结构化数据与其可能执行的功能分开。这促进了松散耦合,并允许在不修改数据类的情况下添加额外的操作。

访问者模式允许您将算法与操作它的相对复杂的对象结构分离。这种分离的结果是一个功能有限的数据模型和一组对数据执行操作的访问者。另一个好处是可以在不修改现有结构的情况下添加新的访问者。数据结构的类是用可以被访问者对象使用的成员创建的。通常,这个数据对象作为其方法(约定是调用这个方法Visit())的参数传递给访问者。

结构代码示例

下面的UML图描述了访问者设计模式的一种实现。该图由六部分组成

  • 客户端:该类是访问者模式的消费者。它管理对象结构,并指示结构内的对象何时接受访问者。
  • ObjectStructure:这是一个包含所有可访问对象的类。它提供了一种遍历所有元素的机制。这个结构不一定是一个集合。它可以是一个复杂的结构,例如一个复合对象。
  • ElementBase:是一个声明accept操作的抽象。这是使对象能够被访问者对象“访问”的入口点。集合中的每个对象都应该实现这个抽象,以便能够被访问。
  • ConcreteElement:这些类继承自抽象基类ElementBase或实现接口并定义accept操作。访问者对象通过accept操作传递给这个对象。
  • VisitorBase:为对象结构中ConcreteElement的每个类声明一个Visit操作。操作的名称和签名标识向访问者发送Visit请求的类。这让访问者确定被访问元素的具体类。然后访问者可以通过其特定接口直接访问元素。
  • ConcreteVisitor:实现Visitor声明的每个操作。每个操作实现为结构中相应类或对象定义的算法的一个片段。ConcreteVisitor提供算法的上下文并存储其局部状态。此状态通常在遍历结构期间累积结果。
static class Program
{
    static void Main()
    {
        var objectStructure = new ObjectStructure();
        objectStructure.Attach(new ConcreteElementA());
        objectStructure.Attach(new ConcreteElementB());
        objectStructure.Accept(new ConcreteVisitorA());
        objectStructure.Accept(new ConcreteVisitorB());
    }
}

public abstract class ElementBase
{
    public abstract void Accept(VisitorBase visitor);
}

public abstract class VisitorBase
{
    public abstract void Visit(ConcreteElementA element);

    public abstract void Visit(ConcreteElementB element);
}

public class ObjectStructure
{
    private List<ElementBase> _elements;

    public ObjectStructure()
    {
        _elements = new List<ElementBase>();
    }

    public void Accept(VisitorBase visitor)
    {
        foreach (ElementBase element in _elements)
        {
            element.Accept(visitor);
        }
    }

    public void Attach(ElementBase element)
    {
        _elements.Add(element);
    }

    public void Detach(ElementBase element)
    {
        _elements.Remove(element);
    }
}

public class ConcreteElementA : ElementBase
{
    public override void Accept(VisitorBase visitor)
    {
        visitor.Visit(this);
    }

    public string Name { get; set; }
}
 
 
public class ConcreteElementB : ElementBase
{
    public override void Accept(VisitorBase visitor)
    {
        visitor.Visit(this);
    }

    public string Title { get; private set; }
}

public class ConcreteVisitorA : VisitorBase
{
    public override void Visit(ConcreteElementA element)
    {
        Console.WriteLine("VisitorA visited ElementA : {0}", element.Name);
    }

    public override void Visit(ConcreteElementB element)
    {
        Console.WriteLine("VisitorA visited ElementB : {0}", element.Title);
    }
}

public class ConcreteVisitorB : VisitorBase
{
    public override void Visit(ConcreteElementA element)
    {
        Console.WriteLine("VisitorB visited ElementA : {0}", element.Name);
    }

    public override void Visit(ConcreteElementB element)
    {
        Console.WriteLine("VisitorB visited ElementB : {0}", element.Title);
    }
}

真实世界示例

在这个例子中,我们有一个Car类,它代表对象结构。这个类包含一个IElementBase对象的通用列表,这些对象代表汽车部件。为了支持访问者设计模式,IElementBase的每个具体实现都包含一个名为Accept的方法,该方法将汽车部件附加到具体访问者。我们有一个具体访问者,它显示有关汽车部件的信息。在这种情况下,每个访问者都必须实现IVisitorBase接口。这个接口包含四个Visit方法的签名。这些方法中的每一个都使用不同类型的汽车部件。

public class Body : IElementBase
{

    public Body(string  name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void Accept(IVisitorBase visitor)
    {
        visitor.Visit(this);
    }
}

public class Engine : IElementBase
{

    public Engine(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void Accept(IVisitorBase visitor)
    {
        visitor.Visit(this);
    }
}
 
public class Transmission : IElementBase
{
    public Transmission(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void Accept(IVisitorBase visitor)
    {
        visitor.Visit(this);
    }
}

public class Wheel : IElementBase
{

    public Wheel(string  name)
    {
        Name = name;
    }

    public string Tire { get; set; }

    public string Name { get; set; }

    public void Accept(IVisitorBase visitor)
    {
        visitor.Visit(this);
    }

    void IElementBase.Accept(IVisitorBase visitor)
    {
        throw new System.NotImplementedException();
    }
}
 
public class CarElementPrintVisitor:IVisitorBase
{
    public void Visit(Wheel wheel)
    {
        Console.WriteLine("Wheel ({0}): {1}",wheel.Name,wheel.Tire);
    }

    public void Visit(Body body)
    {
        Console.WriteLine("Body: {0}",body.Name);
    }

    public void Visit(Engine engine)
    {
        Console.WriteLine("Engine: {0}",engine.Name);
    }

    public void Visit(Transmission transmission)
    {
        Console.WriteLine("Transmission: {0}",transmission.Name);
    }
}

public interface IElementBase
{
    void Accept(IVisitorBase visitor);
}

public class Car : IElementBase
{
    private readonly List<IElementBase> _parts;

    public Car()
    {
        _parts=new List<IElementBase>
                   {
                       new Wheel("Front Left"){Tire = "Michelin Pilot Super Sport"},
                       new Wheel("Front Right"){Tire = "Michelin Pilot Super Sport"},
                       new Wheel("Back Left"){Tire = "Michelin Pilot Super Sport"},
                       new Wheel("Back Right"){Tire = "Michelin Pilot Super Sport"},
                       new Engine("3.3 TDI 225"),
                       new Body("4-door sedan"),
                       new Transmission("5-speed manual")
                   };
    }

    public void Accept(IVisitorBase visitor)
    {
        foreach (var part in _parts)
        {
            part.Accept(visitor);
        }
    }
}

public interface IVisitorBase
{
    void Visit(Wheel wheel);
    void Visit(Body wheel);
    void Visit(Engine wheel);
    void Visit(Transmission wheel);
}

class Program
{
    static void Main()
    {

        var visitor = new CarElementPrintVisitor();

        var car = new Car();
        car.Accept(visitor);
    }
}

历史

  • 9月7日 - 发布原始版本
  • 10月7日 - 修复了SubtractionExpression/ToString方法。
© . All rights reserved.