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

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年.打印.


Abd*_* Sy 8

基本上,策略用于跨多个类对功能进行分组.哦,你的代码中有一个拼写错误

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而不会破坏您的代码,因为您将知道电话能够呼叫.

希望我帮忙.

  • 在我们使用它之前,我们有点难以理解战略.这实际上取决于你想要代码的scalabe.策略的好处在于你可以通过一个共同点来分组不同的类.当我们想要遍历对象时它很有效.想象你在同一页面上有你的facebook或linkedin消息等的feed.如果你想以相同的方式对待每一个(显示消息,评论),用上面的例子替换你可以做类似的事情:foreach($ feedItems as $ i){printMessage($ i); 而不是循环所有的Facebook,然后推特等...... (2认同)