使用策略模式的好处在哪里?
我查看了 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 是属性,该属性是根据这个新类中实现的算法根据需要设置的。 (与已接受的答案中所演示的类似。)
[编辑更新]结论
策略模式有它的用途,但我是 KISS 的坚定信徒,并且倾向于更直接、更少混淆。技术。 主要是因为我想传递易于维护的代码(而且我很可能是必须进行更改的人!)。
I've looked at this explanation on Wikipedia, specifically the C++ sample, and fail to recognize the difference between just defining 3 classes, creating instances and calling them, and that example. What I saw was just placing two other classes into the process and cannot see where there would be a benefit. Now I'm sure I'm missing something obvious (wood for the trees) - could someone please explain it using a definitive real-world example?
What I can make from the answers so far, it seems to me to be just a more complex way of doing this:
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]
The function I refer to above is replaced with another class in which MoveAlong would be attribute which is set according to need based on the algorithm implemented in this new class. (Similar to what is demonstrated in the accepted answer.)
[Edit-update] Conclusion
The Strategy Pattern has it's uses, but I am a strong believer in KISS, and would tend to more straightforward and less obfuscatory techniques. Mostly since I want to pass on easily maintainable code (and 'cos I'll most likely be the one who have to make the changes!).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
关键是将算法分成可以在运行时插入的类。 例如,假设您有一个包含时钟的应用程序。 绘制时钟的方法有很多种,但大多数情况下,底层功能是相同的。 因此,您可以创建一个时钟显示界面:
然后您的 Clock 类连接到计时器并每秒更新一次时钟显示。 所以你会得到类似的东西:
然后在运行时你用正确的显示类实例化你的时钟。 即您可以让 ClockDisplayDigital、ClockDisplayAnalog、ClockDisplayMartian 全部实现 IClockDisplay 接口。
因此,您稍后可以通过创建新类来添加任何类型的新时钟显示,而不必弄乱您的 Clock 类,也不必重写可能会导致维护和调试混乱的方法。
The point is to separate algorithms into classes that can be plugged in at runtime. For instance, let's say you have an application that includes a clock. There are many different ways that you can draw a clock, but for the most part the underlying functionality is the same. So you can create a clock display interface:
Then you have your Clock class that is hooked up to a timer and updates the clock display once per second. So you would have something like:
Then at runtime you instantiate your clock with the proper display class. i.e. you could have ClockDisplayDigital, ClockDisplayAnalog, ClockDisplayMartian all implementing the IClockDisplay interface.
So you can later add any type of new clock display by creating a new class without having to mess with your Clock class, and without having to override methods which can be messy to maintain and debug.
在Java中,您使用密码输入流来解密,如下所示:
但是密码流不知道您打算使用什么加密算法或块大小、填充策略等......新算法将一直添加,因此对它们进行硬编码不实用。 相反,我们传入一个密码策略对象来告诉它如何执行解密......
一般来说,只要有任何知道它什么的对象,您就可以使用策略模式需要做,而不是如何做。 另一个很好的例子是 Swing 中的布局管理器,尽管在这种情况下效果不太好,请参阅 Totally GridBag 一个有趣的插图。
注意:这里有两种模式在起作用,因为流中流的包装是 Decorator。
In Java you use a cipher input stream to decrypt like so:
But the cipher stream has no knowledge of what encryption algorithm you intend to use or the block size, padding strategy etc... New algorithms will be added all the time so hardcoding them is not practical. Instead we pass in a Cipher strategy object to tell it how to perform the decryption...
In general you use the strategy pattern any time you have any object that knows what it needs to do but not how to do it. Another good example is layout managers in Swing, although in that case it didnt work out quite as well, see Totally GridBag for an amusing illustration.
NB: There are two patterns at work here, as the wrapping of streams in streams is an example of Decorator.
战略和决策/选择之间存在差异。 大多数时候,我们会在代码中处理决策/选择,并使用 if()/switch() 结构来实现它们。 当需要将逻辑/算法与使用分离时,策略模式非常有用。
举个例子,考虑一个轮询机制,不同的用户将检查资源/更新。 现在,我们可能希望一些特权用户能够收到更快的周转时间或更多详细信息。 本质上,所使用的逻辑会根据用户角色而变化。 从设计/架构的角度来看,策略是有意义的,但在较低的粒度级别上,它应该始终受到质疑。
There is a difference between strategy and decision/choice. Most of the time a we would be handling decisions/choices in our code, and realise them using if()/switch() constructs. Strategy pattern is useful when there is a need to decouple the logic/algorithm from usage.
As an example, Think of a polling mechanism, where different users would check for resources/updates. Now we may want some of the priveliged users to be notified with a quicker turnaround time or with more details. Essentailly the logic being used changes based on user roles. Strategy makes sense from a design/architecture view point, at lower levels of granularity it should always be questioned.
策略模式允许您在不扩展主类的情况下利用多态性。 本质上,您将所有可变部分放入策略接口和实现中,并将主类委托给它们。 如果您的主对象仅使用一种策略,则几乎与在每个子类中具有抽象(纯虚拟)方法和不同的实现相同。
策略方法提供了一些好处:
缺点是在许多情况下,策略模式是一种过度杀伤力 - switch/case 运算符的存在是有原因的。 考虑从简单的控制流语句(switch/case 或 if)开始,然后仅在必要时才转移到类层次结构,并且如果您有多个维度的可变性,请从中提取策略。 函数指针落在这个连续体中间的某个地方。
推荐阅读:
The strategy pattern allows you to exploit polimorphism without extending your main class. In essence, you are putting all variable parts in the strategy interface and implementations and the main class delegates to them. If your main object uses only one strategy, it's almost the same as having an abstract (pure virtual) method and different implementations in each subclass.
The strategy approach offers some benefits:
The drawback is that in many cases, the strategy pattern is an overkill - the switch/case operator is there for a reason. Consider starting with simple control flow statements (switch/case or if) then only if necessary move to class hierarchy and if you have more than one dimensions of variability, extract strategies out of it. Function pointers fall somewhere in the middle of this continuum.
Recommended reading:
看待这个问题的一种方法是,当您有多种想要执行的操作并且这些操作是在运行时确定的时。 如果您创建策略的哈希表或字典,则可以检索与命令值或参数相对应的策略。 选择子集后,您可以简单地迭代策略列表并连续执行。
一个具体的例子是计算订单的总额。 您的参数或命令将是基本价格、地方税、城市税、州税、地面运输和优惠券折扣。 当您处理订单的变化时,灵活性就会发挥作用 - 一些州不会征收销售税,而其他订单则需要应用优惠券。 您可以动态分配计算顺序。 只要您考虑了所有计算,就可以容纳所有组合而无需重新编译。
One way to look at this is when you have a variety of actions you want to execute and those actions are determined at runtime. If you create a hashtable or dictionary of strategies, you could retrieve those strategies that correspond to command values or parameters. Once your subset is selected, you can simply iterate the list of strategies and execute in succession.
One concrete example would be calculation the total of an order. Your parameters or commands would be base price, local tax, city tax, state tax, ground shipping and coupon discount. The flexibility come into play when you handle the variation of orders - some states will not have sales tax, while other orders will need to apply a coupon. You can dynamically assign the order of calculations. As long as you have accounted for all your calculations, you can accommodate all combinations without re-compiling.
这种设计模式允许在类中封装算法。
使用该策略的类(客户端类)与算法实现解耦。 您可以更改算法实现,或添加新算法,而无需修改客户端。 这也可以动态完成:客户端可以选择他将使用的算法。
例如,想象一个应用程序需要将图像保存到文件中; 图像可以保存为不同的格式(PNG、JPG ...)。 编码算法将在共享相同接口的不同类中实现。 客户类别将根据用户偏好选择一种。
This design pattern allows to encapsulate algorithms in classes.
The class that uses the strategy, the client class, is decoupled from the algorithm implementation. You can change the algorithms implementation, or add new algorithm without having to modify the client. This can also be done dynamically: the client can choose the algorithm he will use.
For an example, imagine an application that needs to save an image to a file ; the image can be saved in different formats (PNG, JPG ...). The encoding algorithms will all be implemented in different classes sharing the same interface. The client class will choose one depending on the user preference.
在维基百科的示例中,这些实例可以传递到一个函数中,该函数不必关心这些实例属于哪个类。 该函数只是对传递的对象调用
execute
,并且知道正确的事情将会发生。策略模式的一个典型例子是文件在 Unix 中的工作方式。 给定一个文件描述符,您可以读取它、写入它、轮询它、查找它、向它发送 ioctl 等,而不必知道您是否正在处理文件、目录、管道、套接字、设备等。(当然,某些操作,如查找,不适用于管道和套接字。但在这些情况下读取和写入可以正常工作。)
这意味着您可以编写通用代码处理所有这些不同类型的“文件”,而不必编写单独的代码来处理文件与目录等。Unix 内核负责将调用委托给正确的代码。
现在,这是内核代码中使用的策略模式,但您没有指定它必须是用户代码,只是一个现实世界的示例。 :-)
In the Wikipedia example, those instances can be passed into a function that doesn't have to care which class those instances belong to. The function just calls
execute
on the object passed, and know that the Right Thing will happen.A typical example of the Strategy Pattern is how files work in Unix. Given a file descriptor, you can read from it, write to it, poll it, seek on it, send
ioctl
s to it, etc., without having to know whether you're dealing with a file, directory, pipe, socket, device, etc. (Of course some operations, like seek, don't work on pipes and sockets. But reads and writes will work just fine in these cases.)That means you can write generic code to handle all these different types of "files", without having to write separate code to deal with files versus directories, etc. The Unix kernel takes care of delegating the calls to the right code.
Now, this is Strategy Pattern as used in kernel code, but you didn't specify that it had to be user code, just a real world example. :-)
策略模式的工作原理很简单,即“优先考虑组合而不是继承”,以便可以在运行时更改策略/算法。 为了说明这一点,我们举一个例子,我们需要根据消息的类型加密不同的消息,例如 MailMessage、ChatMessage 等。
现在,在运行时,您可以使用不同的加密器(例如 CDESEncryptor:public)实例化从 CMessage 继承的不同消息(例如 CMailMessage:public CMessage) C加密器)
Strategy pattern works on simple idea i.e. "Favor Composition over Inheritance" so that strategy/algorithm can be changed at run time. To illustrate let's take an example where in we need to encrypt different messages based on its type e.g. MailMessage, ChatMessage etc.
Now at runtime you can instantiate different Messages inherited from CMessage (like CMailMessage:public CMessage) with different encryptors (like CDESEncryptor:public CEncryptor)