Jay*_*att 17 php oop design-patterns
我似乎无法理解战略模式提供的优势.请参阅下面的示例.
//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());
Run Code Online (Sandbox Code Playgroud)
在上面两个例子中,策略模式将提供哪些优势,以及它比第一种方法更好?我为什么要使用策略模式?
上面的示例代码可能包含错误,请忽略代码.
Gor*_*don 33
战略模式的目的是:
定义一系列算法,封装每个算法,并使它们可互换.策略允许算法独立于使用它的客户端.[GoF的:349]
要理解这意味着什么,你必须(强调我的)
考虑一下您的设计中应该有哪些变量.这种方法与关注重新设计的原因相反.不考虑可能迫使改变设计的内容,而是考虑您希望能够在不重新设计的情况下进行更改.这里的重点是封装不同的概念,这是许多设计模式的主题.[GoF的:29]
换句话说,策略是您可以在运行时插入客户端(另一个对象)以更改其行为的相关代码片段.这样做的一个原因是为了防止每次添加新行为时都必须触摸客户端(参见Open Closed Principle(OCP)和Protected Variation).此外,当您获得足够复杂的算法时,将它们放入自己的类中,有助于遵守单一责任原则(SRP).
我发现你的问题中的例子有点不适合掌握战略模式的用处.注册表不应该有一个printMsg()方法,我无法理解这样的例子.一个更简单的例子是我给出的例子我可以将代码包含到PHP类中吗?(我谈论战略的部分开始于答案的一半).
但无论如何,在你的第一个代码中,Registry实现了方法Func1和Func2.因为我们假设它们是相关的算法,所以让我们假装它们真的是persistToDatabase()并且persistToCsv()有一些东西可以包围我们的想法.我们还想象一下,在应用程序请求结束时,您可以从关闭处理程序(客户端)调用这些方法之一.
但是哪种方法呢?嗯,这取决于你配置的内容,标志显然存储在注册表本身.因此,在您的客户端,您最终会得到类似的结果
switch ($registry->persistStrategy)
{
case 'database':
$registry->persistToDatabase();
case 'csv':
$registry->persistToCsv();
default:
// do not persist the database
}
Run Code Online (Sandbox Code Playgroud)
但是像这样的switch语句很糟糕(参见CleanCode:37ff).想象一下,您的客户要求您添加persistToXml()方法.您现在不仅需要更改您的Registry类以添加其他方法,而且还必须更改客户端以适应该新功能.当OCP告诉我们应该关闭我们的类以进行修改时,你必须改变这两个类.
一种改进persist()方法是在Registry上添加一个通用方法并将switch/case移入其中,这样客户端只需要调用
$registry->persist();
Run Code Online (Sandbox Code Playgroud)
这样更好,但它仍然留给我们切换/案例,它仍然迫使我们每次添加一种新方法来修改注册表.
现在还想象您的产品是许多开发人员使用的框架,他们提出了自己的持久算法.他们怎么能添加它们?他们必须扩展你的课程,但他们还必须替换你使用你的课程中的所有事件.或者他们只是把它们写进你的班级,但是每次你提供新版本时他们都必须修补它.所以这就是一堆蠕虫.
救援战略.由于持久算法是变化的东西,我们将封装它们.既然您已经知道如何定义一系列算法,我将跳过该部分并仅显示生成的客户端:
class Registry
{
public function persist()
{
$this->persistable->persist($this->data);
}
public function setPersistable(Persistable $persistable)
{
$this->persistable = $persistable
}
// other code …
Run Code Online (Sandbox Code Playgroud)
很好,我们用多态来重构条件.现在,您和所有其他开发人员可以设置任何可持续的策略:
$registry->setPersistable(new PersistToCloudStorage);
Run Code Online (Sandbox Code Playgroud)
就是这样.没有更多的开关/箱子.没有更多的注册表黑客.只需创建一个新类并进行设置即可.该策略允许算法独立于使用它的客户端.
另见
[四人帮]灰阶,E.,头盔,R.,约翰逊,R.,Vlissides,J.,设计模式:可复用面向对象软件的基础,阅读,马萨诸塞州:艾迪生韦斯利,1995年.
[CleanCode] Martin,Robert C.清洁代码:敏捷软件工艺手册.上马鞍河,新泽西州:普伦蒂斯大厅,2009年.打印.
基本上,策略用于跨多个类对功能进行分组.哦,你的代码中有一个拼写错误
class context {
public function printMsg(registry $class){
$class->printMsg();
}
}
Run Code Online (Sandbox Code Playgroud)
使用类型提示的界面名称.为了更清楚,让我向您展示一个小例子.想象一下,你有一个Iphone和一个Android他们的共同点是什么?一个是他们都是手机.你可以创建这样的界面
interface Telephone {
public function enterNumber();
public function call();
public function sentTextMessage();
}
Run Code Online (Sandbox Code Playgroud)
并在每部电话中实现界面:
class Iphone implements Telephone {
// implement interface methods
}
class Android implement Telephone {
}
Run Code Online (Sandbox Code Playgroud)
并且您可以在所有电话上附加服务,例如汽车上的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());
Run Code Online (Sandbox Code Playgroud)
作为应用程序开发人员,您将能够在每个实现电话界面的电话上使用handFreeCall而不会破坏您的代码,因为您将知道电话能够呼叫.
希望我帮忙.