我看过 维基百科上的这个解释, ,特别是 C++ 示例,并且无法识别仅定义 3 个类、创建实例并调用它们与该示例之间的区别。我所看到的只是将另外两个类放入该过程中,看不出有什么好处。现在我确信我错过了一些明显的东西(树木为木头)——有人可以用一个明确的现实世界的例子来解释它吗?


到目前为止,我可以从答案中得到什么,在我看来,这只是一种更复杂的方法:

have an abstract class: MoveAlong with a virtual method: DoIt()
have class Car inherit from MoveAlong, 
     implementing DoIt() { ..start-car-and-drive..}
have class HorseCart inherit from MoveAlong, 
     implementing DoIt() { ..hit-horse..}
have class Bicycle inherit from MoveAlong, 
     implementing DoIt() { ..pedal..}
now I can call any function taking MoveAlong as parm 
passing any of the three classes and call DoIt
Isn't this what Strategy intents? (just simpler?)

edit-update]我所指的函数上面的函数被另一个类替换,其中MoveAlong将是根据需要根据此新类实现的算法设置的属性。(类似于已接受的答案中所展示的内容。)


[编辑-更新] 结论

策略模式有其用途,但我坚信 KISS,并且倾向于更直接、更少混淆的技术。主要是因为我想传递易于维护的代码(而且我很可能是必须进行更改的人!)。

有帮助吗?

解决方案

重点是将算法分成可在运行时插入的类。例如,假设您有一个包含时钟的应用程序。您可以通过许多不同的方式绘制时钟,但在大多数情况下,底层功能是相同的。所以你可以创建一个时钟显示界面:

class IClockDisplay
{
    public:
       virtual void Display( int hour, int minute, int second ) = 0;
};

然后你的Clock类连接到一个定时器并每秒更新一次时钟显示。所以你会有类似的东西:

class Clock
{
   protected:
      IClockDisplay* mDisplay;
      int mHour;
      int mMinute;
      int mSecond;

   public:
      Clock( IClockDisplay* display )
      {
          mDisplay = display;
      }

      void Start(); // initiate the timer

      void OnTimer()
      {
         mDisplay->Display( mHour, mMinute, mSecond );
      }

      void ChangeDisplay( IClockDisplay* display )
      {
          mDisplay = display;
      }
};

然后在运行时使用正确的显示类来实例化您的时钟。即你可以使用ClockDisplayDigital,ClockDisplayAnalog,ClockDisplayMartian来实现IClockDisplay接口。

因此,您可以稍后通过创建新类来添加任何类型的新时钟显示,而不必乱丢您的Clock类,并且无需覆盖维护和调试可能很麻烦的方法。

其他提示

在Java中,您使用密码输入流进行解密,如下所示:

String path = ... ;
InputStream = new CipherInputStream(new FileInputStream(path), ???);

但是密码流不知道您打算使用什么加密算法或块大小,填充策略等......新算法将一直添加,因此硬编码不实用。相反,我们传入一个Cipher 策略对象来告诉它如何执行解密......

String path = ... ;
Cipher strategy = ... ;
InputStream = new CipherInputStream(new FileInputStream(path), strategy);

一般情况下,只要有任何对象知道 需要做什么而不是如何,就可以使用策略模式。另一个很好的例子是Swing中的布局管理器,虽然在这种情况下它也没有那么好用,请参阅 Totally GridBag 有趣的插图。

注意:这里有两种模式,因为流中的流包裹是装饰器的一个例子

策略与决策/选择之间存在差异。大多数情况下,我们将在代码中处理决策/选择,并使用if()/ switch()构造实现它们。当需要将逻辑/算法与使用分离时,策略模式很有用。

作为一个例子,想想一个轮询机制,不同的用户会检查资源/更新。现在,我们可能希望通过更快的周转时间或更多详细信息通知一些受益用户。 Essentailly使用的逻辑基于用户角色而改变。策略从设计/架构的角度来看是有意义的,在较低的粒度级别,它应该始终受到质疑。

策略模式允许您在不扩展主类的情况下利用多态性。本质上,您将所有可变部分放入策略接口和实现中,并将主类委托给它们。如果您的主对象仅使用一种策略,则几乎与在每个子类中具有抽象(纯虚拟)方法和不同的实现相同。

该策略方法有一些好处:

  • 您可以在运行时更改策略 - 将此与在运行时更改类类型进行比较,这要困难得多,特定于编译器并且对于非虚拟方法来说是不可能的
  • 一个主类可以使用多种策略,这允许您以多种方式重新组合它们。考虑一个类,该类遍历树并根据每个节点和当前结果评估函数。您可以有一个步行策略(深度优先或广度优先)和计算策略(一些函子 - 即“计算正数”或“求和”)。如果您不使用策略,则需要为步行/计算的每种组合实现子类。
  • 代码更容易维护,因为修改或理解策略不需要您了解整个主要对象

缺点是,在许多情况下,策略模式是一种杀伤力——switch/case 操作符的存在是有原因的。考虑从简单的控制流语句(switch/case 或 if)开始,然后仅在必要时才转移到类层次结构,并且如果您有多个维度的可变性,请从中提取策略。函数指针落在这个连续体中间的某个地方。

推荐阅读:

查看此内容的一种方法是,您希望执行各种操作,并在运行时确定这些操作。如果创建哈希表或策略字典,则可以检索与命令值或参数对应的策略。选择子集后,您可以简单地迭代策略列表并连续执行。

一个具体的例子是计算订单的总和。您的参数或命令将是基本价格,地方税,城市税,州税,地面运费和优惠券折扣。当您处理订单变化时,灵活性会发挥作用 - 某些州不会征收销售税,而其他订单则需要申请优惠券。您可以动态分配计算顺序。只要您考虑了所有计算,就可以适应所有组合而无需重新编译。

此设计模式允许在类中封装算法

使用策略的类(客户端类)与算法实现分离。您可以更改算法实现,或添加新算法而无需修改客户端。这也可以动态完成:客户端可以选择他将使用的算法。

例如,想象一下需要将图像保存到文件的应用程序;图像可以以不同的格式保存(PNG,JPG ...)。编码算法将全部在共享相同接口的不同类中实现。客户端类将根据用户首选项选择一个。

在Wikipedia示例中,这些实例可以传递给一个函数,该函数不必关心这些实例属于哪个类。该函数只是对传递的对象调用 execute ,并且知道正确的事情会发生。

策略模式的典型示例是文件在Unix中的工作方式。给定文件描述符,您可以从中读取,写入,轮询,搜索,向其发送 ioctl 等,而无需知道您是否正在处理文件(目录,管道,套接字,设备等)(当然,有些操作,比如搜索,不能在管道和套接字上工作。但在这些情况下,读写操作会很好。)

这意味着您可以编写通用代码来处理所有这些不同类型的“文件”,而无需编写单独的代码来处理文件与目录等.Unix内核负责将调用委托给正确的代码

现在,这是内核代码中使用的策略模式,但您没有指定它必须是用户代码,只是一个真实的例子。 : - )

策略模式适用于简单的想法,即“赞成组合而不是继承”。这样可以在运行时更改策略/算法。为了说明,我们举一个例子,我们需要根据其类型加密不同的消息,例如: MailMessage,ChatMessage等。

class CEncryptor
{
    virtual void encrypt () = 0;
    virtual void decrypt () = 0;
};
class CMessage
{
private:
    shared_ptr<CEncryptor> m_pcEncryptor;
public:
    virtual void send() = 0;

    virtual void receive() = 0;

    void setEncryptor(cost shared_ptr<Encryptor>& arg_pcEncryptor)
    {
        m_pcEncryptor =  arg_pcEncryptor;
    }

    void performEncryption()
    {
        m_pcEncryptor->encrypt();
    }
};

现在,在运行时,您可以使用不同的加密器(如CDESEncryptor:public CEncryptor)实例化从CMessage继承的不同消息(如CMailMessage:public CMessage)

CMessage *ptr = new CMailMessage();
ptr->setEncryptor(new CDESEncrypto());
ptr->performEncryption();
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top