2008-10-05 28 views
12

我已经看过this explanation on Wikipedia,特别是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?) 

[编辑更新] 我指的是功能上面被替换为另一个类,其中MoveAlong将成为根据需要设置的属性,基于在这个新类中实现的算法。 (什么是证明接受的答案类似。)


[编辑更新] 结论

策略模式有它的用途,但我在吻一个强有力的信徒,会倾向于更直接,更不困惑的技术。大多数情况下,因为我想传递易于维护的代码(并且'我最有可能是必须进行更改的人员!)。

+0

“是不是这是什么策略的意图?(只是更简单?)“< - - 除了你给你的类的引用,让其他类在需要的时候调用它,而另一个类只知道它有一个MoveAlong。这是在团队中更有用的其中一种。 – Gerald 2008-10-05 10:52:30

+0

嗨杰拉尔德我认为你的例子是一个战略模式的坏榜样。是的,它是一种战略,但在你的情况下,有比实施战略模式更好的子类。我相当肯定火星时钟会有不同于地球时钟的行为,因此它值得继承时钟类。 – RWendi 2008-11-27 04:26:53

+0

大声笑...我刚看到这个评论。我想,如果你期望在火星上有客户,那么在你的设计中可能值得担心火星钟。但是由于100%的大多数人的客户将成为地球上的人类,并且具有世界时间数据表示,所以我认为忽略这种边缘情况是相当安全的,除非是视频游戏。 – Gerald 2009-06-05 14:44:30

回答

19

要点是算法分离成可以在运行时插入的类。例如,假设您有一个包含时钟的应用程序。有很多不同的方法可以绘制时钟,但大部分底层功能都是一样的。所以,你可以创建一个时钟显示接口:

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

然后你是迷上了一个计时器,并以每秒一次的更新时钟显示的时钟类。所以你会有这样的东西:

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类,也无需重写可能会混乱维护和调试的方法。

1

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

战略模式的一个典型例子是文件如何在Unix中工作。给定一个文件描述符,你可以读取它,写入它,轮询它,寻找它,发送ioctl等,而不必知道你是否正在处理文件,目录,管道,套接字,设备等(当然,一些操作,如seek,不适用于管道和插座,但在这些情况下,读写操作就可以正常工作)。

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

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

10

在Java中使用一个密码输入流解密,像这样:

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

但密码流没有你打算用什么加密算法知识或块大小,填充等策略...新的算法会一直添加,所以对它们进行硬编码是不实际的。相反,我们传递一个密码策略对象告诉它如何执行解密...

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

一般来说,你使用的策略模式任何时候你有一个知道的任何对象它需要做什么但不是如何来做到这一点。另一个很好的例子是Swing中的布局管理器,尽管在这种情况下它并没有很好地工作,请参阅Totally GridBag以获得有趣的插图。

注意:这里有两种模式,因为流中流的包装是Decorator的示例。

2

这个设计模式允许封装算法在类中。

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

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

4

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

的战略方针提供了一些好处:

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

的缺点是,在许多情况下,策略模式是一个矫枉过正 - 开关/箱运营商这是有原因的。考虑从简单的控制流程语句(开关/案例或if)开始,然后仅在必要时移至类层次结构,并且如果您有多个可变性维度,请从中提取策略。函数指针落在这个连续的中间。

推荐阅读:

5

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

举一个例子,考虑轮询机制,其中不同的用户会检查资源/更新。现在,我们可能希望以更快的周转时间或更多细节通知一些有用的用户。 Essentailly所使用的逻辑根据用户角色而改变。战略从设计/架构的角度来看是有意义的,在较低的粒度水平上,它应该总是被质疑。

2

查看此问题的一种方法是当您想要执行各种操作并在运行时确定这些操作时。如果创建散列表或策略字典,则可以检索与命令值或参数相对应的策略。一旦你的子集被选中,你可以简单地迭代策略列表并连续执行。

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

0

策略模式对简单的想法起作用,即“赞成构造与继承”,以便策略/算法可以在运行时更改。举例说明一下,我们需要根据其类型来加密不同的消息,例如, 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(); 
    } 
}; 

现在在运行时可以实例化不同的消息继承的CMessage(如CMailMessage:公众的CMessage)用不同的加密器(如CDESEncryptor:公共CEncryptor)

CMessage *ptr = new CMailMessage(); 
ptr->setEncryptor(new CDESEncrypto()); 
ptr->performEncryption();