在php中使用策略模式的优点

时间:2013-07-18 11:00:47

标签: 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());

在上面的两个例子中,策略模式将提供哪些优势,以及它比第一种方法更好?我为什么要使用策略模式?

上面的示例代码可能包含错误,请忽略代码。

6 个答案:

答案 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包:

https://packagist.org/packages/pugx/godfather

答案 4 :(得分:0)

四人帮所设想的战略模式的几个重要特征是:

  1. 它没有条件语句(所以省略你的开关,如果,等条件)
  2. 它有一个上下文参与者
  3. 关于该战略的大多数建议都不包括上下文参与者。您可以在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。