我似乎无法理解策略模式提供的优势。请参阅下面的示例。
//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());
在上面的两个例子中,策略模式将提供哪些优势,以及它比第一种方法更好?我为什么要使用策略模式?
上面的示例代码可能包含错误,请忽略代码。
答案 0 :(得分:32)
策略模式的目的是:
定义一系列算法,封装每个算法,并使它们可互换。 策略允许算法独立于使用它的客户端。 [GoF的:349]
要明白这意味着什么,你必须(强调我的)
考虑设计中应该变量的内容。这种方法与关注重新设计的原因相反。而不是考虑可能迫使改变设计的内容,考虑您希望能够在不重新设计的情况下进行更改。这里的重点是封装不同的概念,这是许多设计模式的主题。 [GoF的:29]
换句话说,策略是您可以在运行时插入客户端(另一个对象)以更改其行为的相关代码片段。这样做的一个原因是为了防止每次添加新行为时都必须触摸客户端(参见Open Closed Principle (OCP)和Protected Variation)。此外,当您获得足够复杂的算法时,将它们放入自己的类中,有助于遵守Single Responsibility Principle (SRP)。
我发现你的问题中的例子有点不适合掌握战略模式的用处。注册表不应该有printMsg()
方法,因此我无法理解这个例子。一个更简单的例子是我在Can I include code into a PHP class?中给出的例子(我谈论战略的部分开始于答案的一半)。
但无论如何,在你的第一个代码中,Registry实现了方法Func1和Func2。由于我们假设这些是相关的算法,让我们假设它们真的是persistToDatabase()
和persistToCsv()
来包含我们的想法。我们还想象一下,在应用程序请求结束时,您可以从shutdown handler(客户端)调用其中一种方法。
但是哪种方法?嗯,这取决于你配置的内容,标志显然存储在注册表本身。因此,在您的客户端,您最终会得到类似
的内容switch ($registry->persistStrategy)
{
case 'database':
$registry->persistToDatabase();
case 'csv':
$registry->persistToCsv();
default:
// do not persist the database
}
但是像这样的switch语句很糟糕(参见CleanCode:37ff)。想象一下,您的客户要求您添加persistToXml()
方法。您现在不仅需要更改您的Registry类以添加其他方法,而且还必须更改客户端以适应该新功能。当OCP告诉我们应该关闭我们的类以进行修改时,你必须改变这两个类。
改善这种方法的一种方法是在注册表中添加通用persist()
方法并将交换机/案例移入其中,以便客户端只需要调用
$registry->persist();
这样更好,但它仍然留给我们切换/案例,它仍然迫使我们每次添加一种新方法来修改注册表。
现在还想象您的产品是许多开发人员使用的框架,他们提出了自己的持久算法。他们怎么能添加它们?他们必须扩展你的课程,但他们还必须替换你使用你的课程中的所有事件。或者他们只是把它们写进你的班级,但是每次你提供新版本时他们都必须修补它。所以这就是一堆蠕虫。
救援战略。由于持久算法是变化的东西,我们将封装它们。由于您已经知道如何定义一系列算法,因此我将跳过该部分并仅显示生成的客户端:
class Registry
{
public function persist()
{
$this->persistable->persist($this->data);
}
public function setPersistable(Persistable $persistable)
{
$this->persistable = $persistable
}
// other code …
很好,我们refactored the conditional with polymorphism。现在,您和所有其他开发人员可以将任何可持久性设置为所需策略:
$registry->setPersistable(new PersistToCloudStorage);
就是这样。没有更多的开关/箱子。没有更多的注册表黑客。只需创建一个新类并进行设置即可。该策略允许算法独立于使用它的客户端。
另见
[GoF] Gamma,E.,Helm,R.,Johnson,R.,Vlissides,J.,Design Patterns:Elements of Reusable ObjectOriented Software,Reading,Mass。:AddisonWesley,1995。
[CleanCode] Martin,Robert C.清洁代码:敏捷软件工艺手册。 Upper Saddle River,NJ:Prentice Hall,2009。打印。
答案 1 :(得分:8)
基本上,策略用于跨多个类分组功能。 哦,你的代码中有一个拼写错误
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());
作为应用程序开发人员,您可以在每个实现电话界面的手机上使用handFreeCall而不会破坏您的代码,因为您将知道电话能够呼叫。
希望我帮助过。
答案 2 :(得分:2)
策略模式有助于抽象出用于执行某些特定工作的算法方法。假设您想通过不同的算法对数字数组进行排序,在这种情况下,应用策略模式比将算法与代码紧密耦合更好。
在您实例化由策略组成的类的类中,您通过将策略类引用传递给组合类的构造函数来实例化它。
通过这种方式,我们编程接口而不是实现,因此在任何时候我们都可以向策略类的层次结构中添加更多类,并将其传递给由策略组成的类
请仔细阅读以下link
答案 3 :(得分:0)
这里有一个库和一个symfony包:
答案 4 :(得分:0)
四人帮所设想的战略模式的几个重要特征是:
关于该战略的大多数建议都不包括上下文参与者。您可以在PHP中找到五种不同的策略模式示例:http://www.php5dp.com/category/design-patterns/strategy/
答案 5 :(得分:0)
通常,例子有点奇怪,描述鸭子,猫或其他。 以下是用于显示警报的策略模式示例。 (扩展戈登的答案)。
1。方法的界面各不相同(即案例中的警报格式):
require_once("initialize/initialize.php");
interface alert{
public function alert($message);
};
2。实现警报界面的方法。
class alertBorderBullet implements alert{
public function alert($message){
$alert = "<p style='border:1px solid #eee; padding:4px; padding-left:8px; padding-right:8px; border-left:4px solid #FC0; margin-top:8px; margin-bottom:8px; color:#888'>".$message."</p>";
return $alert;
}
};
class alertOrangeBgnd implements alert{
public function alert($message){
$alert = "<p style='color:#fff; background-color:#ff9c3a; padding:4px; padding-left:8px; padding-right:8px; margin-top:8px; margin-bottom:8px; border-left:4px solid #e471bd;'>".$message."</p>";
return $alert;
}
};
class alertRed implements alert{
public function alert($message){
$alert = "<p style='color:#c11; background-color:#efefef; padding:4px; padding-left:12px; padding-right:8px; margin-top:8px; margin-bottom:8px;'>".$message."</p>";
return $alert;
}
};
3。 Messenger,用于分隔警报方法设置和从项目中的其他对象获取。
class alertMessenger{
protected $_alert;
public function setAlertType(alert $alert){$this->_alert = $alert;}
public function returnAlert($message){return $this->_alert->alert($message);}
};
4。随机项目对象,将以不同方式使用“警报”。
class randomObject{
public $alert;
public function __construct(){
$this->alert = new alertMessenger;
}
// More code here...
};
$randomObject = new randomObject;
$randomObject->alert->setAlertType(new alertRed);
echo $randomObject->alert->returnAlert($message="Red text for critical info");
$randomObject->alert->setAlertType(new alertBorderBullet);
echo $randomObject->alert->returnAlert($message="Border, bullet and pale-color text");
echo $randomObject->alert->returnAlert($message="Repeat, check style permanence");
$randomObject->alert->setAlertType(new alertOrangeBgnd);
echo $randomObject->alert->returnAlert($message="Again, another redefined message style");
randomObject,初始化时(对于本例)会自动创建一个alertMessanger实例并使其方法可用。可以设置行为并回显消息。 其他警报格式可以在必要时由setAlertType创建并使用,然后使用returnAlert。