问题描述
我似乎无法掌握策略模式提供的优势.请参阅下面的示例.
//Implementation without the strategy pattern class Registry { public function Func1(){ echo 'called function 1'; } public function Func2(){ echo 'called function 2'; } } $client = new Registry(); $client->Func1(); $client->Func2(); //Implementation with strategy pattern interface registry { public function printMsg(); } class Func1 implements registry { public function printMsg(){ echo 'called function 1'; } } class Func2 implements registry { public function printMsg(){ echo 'called function 2'; } } class context { public function printMsg(Registry $class){ $class->printMsg(); } } $client = new context(); $client->printMsg(new Func1()); $client->printMsg(new Func2());
在上面的两个示例中,策略模式将提供什么优点,比第一种方法更好?我为什么要使用策略模式?
上面的示例代码可能包含错误,请忽略代码.
推荐答案
策略模式的意图是:
定义一个算法家族,封装了每个算法,并使它们可以互换. 策略使该算法与使用它的客户的独立性不同. [GOF:349]
要了解这意味着什么,您必须(强调我的)
考虑您的设计中应该是什么.这种方法是关注重新设计原因的相反. 不要考虑可能迫使更改的是什么,而是考虑您想在不重新设计的情况下进行更改.这里的重点是封装各种概念,这是许多设计模式的主题. [GOF:29]
换句话说,策略是您可以在运行时插入客户端(另一个对象)的相关代码来改变其行为.这样做的原因之一是,要防止您每次添加新行为时都必须触摸客户(cf. 开放式原理(OCP)和受保护变体).此外,当您获得足够复杂的算法,将它们放入自己的课堂上时,帮助遵守单个责任原则(单个责任原则) srp).
我在您的问题中发现了一个不适合掌握策略模式有用性的示例.注册表不应具有printMsg()方法,我对这样的示例没有太多意义.一个简单的例子就是我在 can将代码包括在PHP类中?(我谈论策略的部分开始答案的一半).
但是,无论如何,在您的第一个代码中,注册表实现了方法func1和func2.由于我们假设这些是相关的算法,所以让我们假装它们确实是persistToDatabase()> and persistToCsv(),可以使我们的思想缠绕.我们还可以想象,在申请请求的末尾,您可以从a 关闭处理程序(客户端).
但是哪种方法?好吧,这取决于您配置的内容和标志显然存储在注册表本身中.因此,在您的客户中,您最终会得到
之类的东西switch ($registry->persistStrategy) { case 'database': $registry->persistToDatabase(); case 'csv': $registry->persistToCsv(); default: // do not persist the database }
但是这样的切换语句不好(参见清洁码:37ff).想象一下您的客户要求您添加persistToXml()方法.您现在不仅需要更改注册表类才能添加另一种方法,而且还必须更改客户端以适应该新功能.当OCP告诉我们应该关闭我们的课程以进行修改时,这是您必须更改的两个类.
改进的一种方法是在注册表上添加通用persist()方法,然后将开关/情况移动到其中,以便客户只需要调用
$registry->persist();
最好,但它仍然使我们拥有开关/情况,它仍然迫使我们每次添加一种坚持下去的新方法时都要修改注册表.
现在还可以想象您的产品是许多开发人员使用的框架,他们提出了自己的持久算法.他们如何添加它们?他们必须延长您的课程,但后来他们还必须替换使用您使用的框架中的所有事件.或者他们只是将它们写入您的课堂,但是每次您提供新版本时,他们都必须修补课程.所以这就是一罐蠕虫.
营救的策略.由于持久性算法是变化的内容,因此我们将封装它们.由于您已经知道如何定义算法家庭,因此我会跳过该部分,仅显示结果客户端:
class Registry { public function persist() { $this->persistable->persist($this->data); } public function setPersistable(Persistable $persistable) { $this->persistable = $persistable } // other code …
很好,我们用多态性重构了条件.现在,您和其他所有开发人员都可以设置任何持久的策略:
$registry->setPersistable(new PersistToCloudStorage);
就是这样.没有更多的开关/情况.不再有注册表黑客.只需创建一个新类并设置它即可.该策略使该算法与使用它的客户的独立性不同.
也请参见
- 该策略模式如何工作?
- -in-Strategy-Pattern
- http://sourcemaking.com/design_patterns/strategy 更多解释
- https://www.youtube.com/watch?v=-ncgrd9-- C6O
结束注:
[GOF] Gamma,E.,Helm,R.,Johnson,R.,Vlissides,J.,设计模式:可重复使用的逆向软件的元素,Mass.:Addisonwesley,1995年.
[Cleancode] Martin,RobertC.清洁代码:敏捷软件手工艺手册.新泽西州上萨德尔河:Prentice Hall,2009年.印刷.
其他推荐答案
基本上是用于分组多个类的功能. 哦,您的代码中有一个错别字
class context { public function printMsg(registry $class){ $class->printMsg(); } }
带有类型提示的界面名称. 更清楚,让我向您展示一个小例子. 想象您有iPhone和Android 他们的共同点是什么? 一个是它们都是手机. 您可以创建一个这样的接口
interface Telephone { public function enterNumber(); public function call(); public function sentTextMessage(); }
并在每个电话中实现接口:
class Iphone implements Telephone { // implement interface methods } class Android implement Telephone { }
,您可以在所有手机上附上一项服务,例如汽车上的GPS: 并确保如果插入电话(通常使用接口蓝牙). 您可以做这样的事情:
class carGps{ public function handFreeCall(Telephone $tel){ $this->amplifyVolume($tel->call()); } } $tomtom = new CarGps(); $tomtom->handFreeCall(new Iphone()); //or if you like to: $tomtom->handFreeCall(new Android());
作为应用程序开发人员,您将能够在实现电话界面的所有手机上使用手法,而无需破坏您的代码,因为您会知道电话能够致电.
希望我有帮助.
其他推荐答案
策略模式有助于抽象出进行某些特定工作的算法方法.假设您想通过不同的算法对数字进行分类,在这种情况下,应用策略模式要比将算法紧紧搭配到代码更好.
在您正在实例化策略组成的类中的课程中,您通过将其传递给组成类的构造函数来实例化策略类参考.
这样,您将我们编程为接口而不是实现,因此在任何时间点我们都可以在策略类的层次结构中添加更多类,并将其传递给由策略组成的类
请通过以下 link
问题描述
I can't seem to get my head around what advantages the strategy pattern offer. See the example below.
//Implementation without the strategy pattern class Registry { public function Func1(){ echo 'called function 1'; } public function Func2(){ echo 'called function 2'; } } $client = new Registry(); $client->Func1(); $client->Func2(); //Implementation with strategy pattern interface registry { public function printMsg(); } class Func1 implements registry { public function printMsg(){ echo 'called function 1'; } } class Func2 implements registry { public function printMsg(){ echo 'called function 2'; } } class context { public function printMsg(Registry $class){ $class->printMsg(); } } $client = new context(); $client->printMsg(new Func1()); $client->printMsg(new Func2());
In the above two example what advantages will the strategy pattern will offer and how is it better then the first approach? Why should I use strategy pattern?
The above example code might contain errors please ignore the code.
推荐答案
The intent of the Strategy pattern is to:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. [GoF:349]
To understand what this means, you have to (emphasis mine)
Consider what should be variable in your design. This approach is the opposite of focusing on the cause of redesign. Instead of considering what might force a change to a design, consider what you want to be able to change without redesign. The focus here is on encapsulating the concept that varies, a theme of many design patterns. [GoF:29]
In other words, strategies are related pieces of code you can plug into a client (another object) at runtime to change its behavior. One reason to do this, is to prevent you from having to touch the client each time a new behavior is added (cf. Open Closed Principle (OCP) and Protected Variation). In addition, when you got sufficiently complex algorithms, putting them into their own classes, helps adhering to the Single Responsibility Principle (SRP).
I find the example in your question somewhat ill-suited to grasp the usefulness of the Strategy Pattern. A Registry should not have a printMsg() method and I cannot make much sense of the example as such. An easier example would be the example I give in Can I include code into a PHP class? (the part where I talk about Strategy begins about halfway down the answer).
But anyway, in your first code, the Registry implements the methods Func1 and Func2. Since we assume these to be related algorithms, let's pretend they really are persistToDatabase() and persistToCsv() to have something to wrap our mind around. Let's also imagine that, at the end of an application request, you call one of these methods from a shutdown handler (the client).
But which method? Well, that depends on what you configured and the flag for that is obviously stored in the Registry itself. So in your client you end up with something like
switch ($registry->persistStrategy) { case 'database': $registry->persistToDatabase(); case 'csv': $registry->persistToCsv(); default: // do not persist the database }
But switch statements like this are bad (cf. CleanCode:37ff). Imagine your customer requests you to add a persistToXml() method. Not only do you have to change your Registry class now to add another method, but you also have to change the client to accommodate for that new feature. That's two classes you have to change, when OCP tell us that our classes should be closed for modification.
One way to improve that would be to add a generic persist() method on the Registry and move the switch/case into it so the client only needs to call
$registry->persist();
That's better but it still leaves us with the switch/case and it still forces us to modify the Registry each time we add a new way to persist it.
Now also imagine your product is a framework used by many developers and they come up with their own persist algorithms. How can they add them? They'd have to extend your class but then they'd also have to replace all the occurrences in the framework where yours was used. Or they just write them into your class, but then they'd have to patch the class each time you provided a new version of it. So that's all a can of worms.
Strategy to the rescue. Since the persist algorithms are the stuff that varies, we will encapsulate them. Since you already know how to define a family of algorithms, I'll skip that part and only show the resulting client:
class Registry { public function persist() { $this->persistable->persist($this->data); } public function setPersistable(Persistable $persistable) { $this->persistable = $persistable } // other code …
Nice, we refactored the conditional with polymorphism. Now you and all the other developers can set whatever Persistable as the desired Strategy:
$registry->setPersistable(new PersistToCloudStorage);
And that's it. No more switch/case. No more Registry hacking. Just create a new class and set it. The Strategy lets the algorithm vary independently from clients that use it.
Also see
- How does the Strategy Pattern work?
- https://softwareengineering.stackexchange.com/questions/187378/context-class-in-strategy-pattern
- http://sourcemaking.com/design_patterns/strategy for some more explanation.
- https://www.youtube.com/watch?v=-NCgRD9-C6o
End Notes:
[GoF] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns: Elements of Reusable ObjectOriented Software, Reading, Mass.: AddisonWesley, 1995.
[CleanCode] Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Upper Saddle River, NJ: Prentice Hall, 2009. Print.
其他推荐答案
Basically Strategy is for grouping functionality across multiple classes. Oh and you have a typo in your code
class context { public function printMsg(registry $class){ $class->printMsg(); } }
With the name of the interface of the type hinting. To be more clear let me show you a small example. Imagine you have an Iphone and an Android What's their common point? One is They are both phones. you could create an interface like this
interface Telephone { public function enterNumber(); public function call(); public function sentTextMessage(); }
and implement the interface in each of your telephones:
class Iphone implements Telephone { // implement interface methods } class Android implement Telephone { }
and you can have a service attached to all phones like a GPS on you car: and be sure that if you plug a telephone (usually with the interface BlueTooth). you could do something like this:
class carGps{ public function handFreeCall(Telephone $tel){ $this->amplifyVolume($tel->call()); } } $tomtom = new CarGps(); $tomtom->handFreeCall(new Iphone()); //or if you like to: $tomtom->handFreeCall(new Android());
as an application developer, you'll be able to use your handFreeCall on every phones that implements the Telephone interface without breaking your code, because you'll know that the telephone is capable of calling.
Hope I helped.
其他推荐答案
Strategy pattern helps in abstracting out the algorithmic approach for doing some particular work. Suppose you want to sort an array of numbers via different algorithms, In this case it is better to apply a strategy pattern than to tightly couple the algorithm with your code.
In the class where you are instantiating the class composed of strategy you instantiate the strategy class reference by passing it to the constructor of composed class.
This way you we are programming to an interface and not implementation and therefore at any point of time we can add more classes to the hierarchy of strategy class and pass the same to the class composed of strategy
Kindly go through the following link