【问题标题】:Which programming design pattern would suit this project?哪种编程设计模式适合这个项目?
【发布时间】:2015-07-19 21:18:25
【问题描述】:

您好,我正在寻找适合我当前项目的编程设计模式的指导。

几个月来,我一直在努力寻找一个不错的项目来开始正确使用模式,而我刚刚开始的一个小项目似乎提供了一个完美的学习平台。

基本上,我正在创建一个可以响应用户命令的 Telegram Bot。这不是我遇到问题的部分,而是如何最好地构建我的代码,以便添加新命令时干净且结构良好。

我不需要任何人为我编写任何代码,但是我可以实现适合的设计模式吗?

这是我目前在 puesudo 中所做的(顺便说一句,我正在使用 Laravel)代码:

//routes.php

  Route::post('inbound', ['uses' => 'inboundController@marshall']);



 //inboundController.php

   public function marshall($inboundMessage){

    //Extract the command from the inbound message eg "start"
    $command = extractfromtext($text);

    //Get the user id from the person who sent the message
    $userID = extractIdfromtext($text);

    //Compare the command in a switch statement

    switch ($command){

    case (start):
        return $result = new commandStart($userID)->fire();
        break;
    case (demo):
        return $result = new commandDemo($userID)->fire();
        break;
    case (another):
        return $result = new commandAnother($userID)->fire();
        break;
    default:
       break;

    }

   } 


//Each command has it's own class:
//Class commandStart

   public function __construct($userID){
       $this->userID = $userID
   }

   publin fuction fire(){

     //send a picture to the userID

   }



//Class commandDemo

   public function __construct($userID){
       $this->userID = $userID
   }

   publin fuction fire(){

     //send a message to the userID

   }



//Class commandAnother

   public function __construct($userID){
       $this->userID = $userID
   }

   publin fuction fire(){

     //send a video to the userID

   }

这很好,但我觉得

  • 我重复了很多代码(不好)
  • 添加新命令意味着不断编辑“入站类”文件。我认为这打破了 SOLID 中的开放封闭原则。
  • switch 语句对我来说有一种“代码味道”。

我对不同类型模式的所有名称感到非常困惑。有人会建议我,哪一个适合这种情况?我很高兴去研究它以及它是如何实现的,但我不想去追逐一个不合适的,因为我不知道更好!

谢谢。


编辑:似乎在我试图使问题泛化时,我把它变得太迟钝了。我已经在使用 MVC 和 Larvel。这不是问题我认为 - 我想知道哪种设计模式可以让我轻松干净地添加更多命令 - 希望不必使用 switch 语句。

在阅读了一些回复之后,我想我开始明白该怎么做了。出于兴趣 - 下面建议的结构是实际设计模式还是只是良好的结构化代码?

【问题讨论】:

  • 你尝试过MVC(Model-View-Controller)模型吗?
  • 利用 function_exists() 和 class_exists(),这样添加新命令只需要一个带有 fire 方法的新类。它们都可以扩展一个抽象类,该类可以定义您的通用构造函数并强制子级实现 fire 方法
  • 对不起,我不能帮助你使用 laravel,我不熟悉它。它被称为Abstract Factory Design Pattern 的设计模式

标签: php oop laravel design-patterns


【解决方案1】:

除了 arkascha 悲伤之外,我还建议您为命令使用抽象类

abstract class Command {

    public function __construct( $params ){
        // do some common things with $params
        $this->do_command();
    }

    protected function do_command(){
         // empty function which will be overwritten by child classes
    }

}

你的孩子会是这样的

class FooCommand extends Command {

    public function __construct( $params ){
        parent::__construct( $params );
        // do something as well
    }

    // can be public
    public function do_command(){
         echo __CLASS__, '::FOO';
    }
}

class BarCommand extends Command {

    // or without __constructor as mentioned by scrowler if you dont
    // need to do additional coding with __contructor params

    // can be protected
    protected function do_command(){
         echo __CLASS__, '::BAR';
    }
}

class BazCommand extends Command {

    public function __construct( $params ){
        parent::__construct( $params );
        // do something as well
    }

    // it cannot be private
    private function do_command(){
         echo __CLASS__, '::BAZ';
    }
}

【讨论】:

  • 子类不需要构造函数。将它放在父级中就可以了,因此您不必在子级中定义它们
  • 如果您需要以不同方式处理每个孩子的 $params,您可能需要它。在示例中添加评论
  • 我建议,如果需要做的事情在构造函数中不是标准的,那么它们被分解成自己的 setter 和 getter,以保持类的骨骼对所有孩子都相同
  • @Mujnoi Gyula Tamas - 非常感谢您分解抽象工厂的解释。这很有帮助。
【解决方案2】:

请查看命令设计模式。您还可以按照之前的建议使用 抽象工厂模式 来实例化要执行的命令。

详细说明我的答案。 让我们将入站控制器称为我们的客户端类。控制器的职责是捕获请求并将其路由到适当的操作,在这种情况下触发命令。 但是如果你看一下代码,控制器实际上是在决定在它接收的某些输入中实例化什么类型的命令。如果由于某种原因命令类的构造函数发生更改,那么您还必须更改入站控制器类中的代码,因为它现在还负责实例化要执行的命令对象。 如果你实现了一个抽象工厂并注入到你的控制器,那么你将必要的输入传递给你的工厂来决定应该实例化哪个命令并将其传递给控制器​​。控制器只是触发它从工厂接收到的命令,而不必担心命令是如何实例化的或它是什么类型的命令。控制器收到执行命令的请求,将命令的实例委托给工厂,一旦工厂将命令交给控制器,它就会被执行。 最好的单一责任原则。抽象工厂负责实例化命令。控制器负责处理请求,在这种情况下,执行工厂返回的命令。

通过查看代码,我们已经可以看出他正在以某种方式使用命令设计模式。这是来自维基百科的定义:Command design pattern

【讨论】:

  • 您能否详细说明一下这个答案?
  • 你好@scrowler 我已经详细阐述了我的回答
  • 我非常喜欢用于处理抽象工厂模式输出的命令设计模式的想法。我可以在脑海中想象出来。我真的不知道我已经在使用命令模式,只是遵循 Laravel 文档中的示例,这些示例为许多命令建议了这种类型的方法,所以我决定使用它!
【解决方案3】:

您在代码中实现的是 MVC 路由器。目前您对路线进行硬编码。相反,您也可以依靠自动加载来为您的控制器实例选择正确的类。如果该类不存在,则回退将使事情变得健壮。两大优势:

  • 易于扩展,只需将控制器类添加到按命名约定命名的文件中

  • 添加另一个控制器时,您根本不必触摸路由器

您会在互联网上找到许多关于此的示例。自动加载功能也有据可查。

玩得开心!

【讨论】:

    猜你喜欢
    • 2023-03-10
    • 2012-10-29
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    相关资源
    最近更新 更多