【问题标题】:A controller that might violate Single Responsibility Principle?可能违反单一职责原则的控制器?
【发布时间】:2014-07-31 20:08:45
【问题描述】:

参考此评论,

当一个类有很长的参数列表时,它可以是一个“代码 闻到“你的班级试图做的太多而且可能没有 遵循单一责任原则。如果你的班级正在尝试 做太多,考虑将你的代码重构为一些 相互消耗的小类。

我应该如何处理下面的这个控制器类 - 它是“试图做太多”吗?

class Controller
{
    public $template;
    public $translation;
    public $auth;
    public $article;
    public $nav;

    public function __construct(Database $connection, $template) 
    {
        $this->template = $template;
        $this->translation = new Translator($connection);
        $this->nav = new Nav($connection);
        $this->article = new Article($connection);
        $this->auth = new Auth($connection);
    }

    public function getHtml() 
    {
        if(isset($_REQUEST['url'])) 
        {
            $item = $this->article->getRow(['url' => 'home','is_admin' => $this->auth->is_admin]);
            include $this->template->path;
        }
    }
}

我如何将它分解为更小的类 - 如果它是一个控制器,它包含我需要输出页面的这些基本类?

我应该怎么做才能遵循dependency injection的原则?

【问题讨论】:

  • 两个参数就好了。然而,所有这些实例化都应该是构造函数或单独设置器的参数。
  • @halfer:谢谢。是的 all those instantiations 在我的控制器中很难看,我怎样才能让它们分开设置器,以便我可以在我的控制器类中调用它们?
  • @halfer:如果我把它们设为parameters either to the constructor,那么它是a class has a very long list of arguments, it can be a "code smell" that your class is trying to do too much and possibly not following the single responsibility principle. 不是吗?
  • (虽然代码格式看起来不错,但作为一般的荧光笔,无论是在问题还是在 cmets 中,它的可读性都不是很好。对这里编辑问题感兴趣的人倾向于建议它只适用于内联代码)。跨度>
  • 是的,不要将它们全部添加到构造函数中。构造函数可能有 2 或 3 个对对象的构造至关重要的参数,其余的应该在 setter 中。 setter 是一个接受参数并将其存储在属性中的函数,可能在验证它之后。如果它们是类或数组,您也可以在 PHP 中对它们进行类型提示。

标签: model-view-controller dependency-injection solid-principles single-responsibility-principle php-5.5


【解决方案1】:

注意:这将是简短的版本,因为我在工作。晚上我会详细说明

所以...您的代码有以下违规行为:

  • SRP(以及扩展 - SoC):您的控制器负责验证输入、授权、数据收集、用数据填充模板并呈现所述模板。此外,您的Article 似乎同时负责数据库抽象域逻辑。

  • LoD:您正在传递$connection,因为您需要将它传递给其他结构。

  • 封装:您的所有类属性都具有公开可见性,并且可以随时更改。

  • 依赖注入:虽然你的“控制器”有几个直接依赖,但你只是传入了模板(实际上不应该由适当的 MVC 中的控制器管理)。

  • 全局状态:你的代码依赖于$_REQUEST superglobal。

  • 松散耦合:您的代码直接与类的名称以及您在构造函数中初始化的这些类的构造函数的足迹相关联。

【讨论】:

  • 感谢您的回答。你能解释一下如何改进我的控制器吗?谢谢!
  • 另外,你能看看我的新帖子,看看我的方向是否正确吗? codereview.stackexchange.com/questions/58709/… 谢谢!
  • LoD 代表什么?
  • “得墨忒耳法则”
【解决方案2】:

TL;DR:我在这里没有看到违反 SRP,但对象组合略有损坏

据我所见(这是完整的类列表吗?),$connection 没有直接在类中使用,因此不应注入。

而且我在任何地方都看不到$this->translation$this->nav 的用法。那是复制粘贴的神器吗?

宁可注入$connection,我会在这个类之外构造ArticleAuth,然后只注入这些,因为你的控制器只直接使用这些,而不是控制器。 所以整个班级会是这样的:

class Controller
{
    private $article;
    private $auth;
    private $template;

    public function __construct(Article $article, Auth $auth, $template) 
    {
        $this->article = $article;
        $this->auth = $auth;
        $this->template = $template;
    }

    public function getHtml() 
    {
        if(isset($_REQUEST['url'])) 
        {
            $item = $this->article->getRow(['url' => 'home','is_admin' => $this->auth->is_admin]);
            include $this->template->path;
        }
    }
}

除非您的Article 是具有ActiveRecord 模式的域对象,否则我仍然会注入$connection 并将其存储在局部变量中。并且仅在您真正需要它时创建新的Article 对象,即在getHtml 方法中。

这样你就不用在构造函数中做太多的工作,只分配局部变量。对象组合在其他地方处理。如果需要,您可以替换 Auth 的实现。

此外,当您不在构造函数中做太多工作时,您的对象图创建非常便宜。如果您使用某种 DI 容器,当必须同时创建大量对象时,这一点很重要。

【讨论】:

  • 感谢您的回答。如果我遵循这种模式,我在构造函数中的注入对于某些控制器来说会很长。
  • 对于依赖太多的控制器,这是一个明确的代码异味和可能的 SRP 违规。但不在此示例中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 2010-10-22
  • 2023-03-23
  • 1970-01-01
相关资源
最近更新 更多