策略模式的意图是:
定义一组算法,将每个算法封装起来,使它们可以互换。策略模式让算法独立于使用它的客户端而变化。[GoF:349]
要理解这是什么意思,你需要(我强调):
考虑在设计中应该是可变的内容。这种方法与专注于重新设计原因相反。不是考虑什么可能会迫使设计发生变化,而是考虑不需要重新设计就可以进行更改的内容。这里的重点是封装变化的概念,这是许多设计模式的主题。[GoF:29]
换句话说,策略是相关的代码片段,您可以在运行时将其插入客户端(另一个对象)以更改其行为。这样做的一个原因是防止每次添加新行为时都必须触及客户端(参见
开放封闭原则(OCP)和
受保护的变化)。此外,当您拥有足够复杂的算法时,将它们放入自己的类中有助于遵循
单一职责原则(SRP)。
我认为您问题中的示例不太适合理解策略模式的有用性。注册表不应该有
printMsg()
方法,我对该示例的意义无法理解。一个更简单的示例是我在
我可以将代码包含到PHP类中吗?中提供的示例(其中我谈论策略的部分大约在答案的中间位置)。
无论如何,在您的第一段代码中,注册表实现了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告诉我们,我们的类应该关闭修改。为了改善这种情况,一种方法是在Registry上添加通用的
persist()
方法,并将switch/case移动到其中,这样客户端只需要调用此通用方法即可。
$registry->persist();
这样做已经好了一些,但是它仍然需要我们使用switch/case,并且每次添加新的持久化方式时都需要修改注册表。
现在想象一下,如果你的产品是一个被许多开发者使用的框架,并且他们想出了自己的持久化算法,那该怎么办呢?他们必须扩展你的类,但是他们还必须替换框架中所有使用你的类的地方。或者他们只是将它们写入你的类中,但是每次你提供新版本时,他们都必须打补丁来更新类。所以这就是一堆麻烦事。
策略模式来拯救。由于持久化算法是变化的内容,我们将对其进行封装。由于您已经知道如何定义一组算法,因此我将跳过该部分,仅显示结果客户端:
class Registry
{
public function persist()
{
$this->persistable->persist($this->data);
}
public function setPersistable(Persistable $persistable)
{
$this->persistable = $persistable
}
// other code …
很好,我们
使用多态性重构了条件语句。现在你和其他开发者都可以将任何Persistable设置为所需的策略:
$registry->setPersistable(new PersistToCloudStorage)
这就是全部内容了。不再使用switch/case和注册表修改,只需创建一个新类并设置即可。策略模式使算法能够独立于使用它的客户端而变化。
另请参见:
-
策略模式是如何工作的?
-
https://softwareengineering.stackexchange.com/questions/187378/context-class-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., Design Patterns: Elements of Reusable ObjectOriented Software, Reading, Mass.: AddisonWesley, 1995.
[CleanCode] 马丁,罗伯特C.《代码整洁之道:敏捷软件工艺宝典》。新泽西州,上沙德尔河: 普林斯顿大学出版社,2009年。印刷版。