【问题标题】:SOLID Principle In Laravel with Repository PatternLaravel 中的 SOLID 原理与存储库模式
【发布时间】:2016-03-03 04:05:02
【问题描述】:

我对在保持 SOLID 原则的同时将控制器与存储库模式一起使用有些困惑。考虑一下,我有两种类型的报价

  1. 商业报价
  2. 私人报价

而且未来很有可能出现新的报价类型。每个报价都有不同的领域、业务逻辑,但它们共享许多共同的功能。所以我创建了一个 QuotationInterface

报价界面

interface QuotationInterface
{   
    public function save(array $data);

}

实现接口的引用类

class CommercialQuotation implements QuotationInterface
{   
    public function(array $data)
    {
        // save commercial quotation
    }
}

class PrivateQuotation implements QuotationInterface
{   
    public function(array $data)
    {
    // save Private quotation
    }
}

报价库

class QuotationRepository 
{
    public function save(array $data, QuotationInterface $quotation)
    {
        $quotation->save($data);
    }
}

QotationController

public function store(Resource $resource)
{

    $inputs = $resource->all();

    /**
    *  Clearly here Open/Close Principle is broken
    */

    if ($inputs['type'] == 'private'){
        $quotation = new PrivateQuotation;;
    }
    else if($inputs['type'] == 'commercial'){
        $quotation = new CommercialQuotation;
    }

    $this->repo->save($inputs, $quotation);
}

在我的 QuotationController 中,显然违反了 Open/Close 原则..

为每种类型的报价创建一个控制器是个好主意(有一天可能会超过 10 个,谁知道?)以避免违反 OCP 或我的设计是错误的?欢迎任何建议、设计更改提示、资源。

注意:我的报价控制器除了保存之外还有许多其他功能。

【问题讨论】:

    标签: oop laravel design-patterns dry solid-principles


    【解决方案1】:

    我知道已经晚了,但我希望我的评论能帮助更多人搜索您的问题。

    你应该遵循以下原则:

    “如果你有这样的条件,你应该在低层抽象而不是高层”

    所以设计应该是:

    class QuotationFactory
    {   
        public static function make($type)
        {
            switch($type)
            {
                CASE "private":
                    return new PrivateQuotation();
                CASE "commercial":
                    return new CommercialQuotation();
                default:
                    throw new InvalidTypeException("Invalid type code.");
            }
        }
    }
    

    上述重构称为“用多态替换条件/类型代码”。

    如果以后没有新类型,你应该遵循“用显式方法替换参数”重构,它会提高可读性。

    有关更多信息,您应该阅读 Martin Fowler 撰写的名为“重构:改进现有代码的设计”的书。

    【讨论】:

      【解决方案2】:

      如果您按照您展示的方式进行,我建议您使用单个控制器进行报价,并使用 Factory 设计模式来创建您的 $quotation 对象

      例如,像这样的简单工厂:

      //FACTORY CLASS
      abstract class QuotationFactory
      {
          /** return QuotationInterface */
          public static function createQuotation($type)
          {
              switch($type)
              {
                  CASE "private":
                      return new PrivateQuotation();
                  break;
      
                  CASE "commercial":
                      return new CommercialQuotation();
                  break;    
              }
          }
      }
      

      您可以使用控制器方法中的工厂:

      //YOUR CONTROLLER'S METHOD
      public function store(Resource $resource)
      {   
          $inputs = $resource->all();
      
          //delegate objects creation to factory class
          $quotation = QuotationFactory::createQuotation( $inputs['type'] );
      
          $this->repo->save($inputs, $quotation);
      }
      

      这样你就不会违反控制器中的开/关原则,因为当你添加引号时,你只需要修改工厂的方法(在 switch 语句中添加案例),它会在需要的地方返回一个QuotationFactory 对象。

      这也将使您的代码保持 DRYSOLID,因为您不必在委托时重复 if/else 语句来在控制器的方法中创建对象对象创建对特定工厂类的责任

      正如下面 cmets 中正确指出的那样,简单工厂将帮助您避免控制器中的开/关原则,但请注意,从更一般的角度来看,简单工厂本身就违反了OCP,因为它使用开关盒。

      无论如何,从我对您的应用程序的了解来看,简单工厂可能是一个很好的解决方案,因为您主要关心的是在许多地方从变量类型构建实例。因此,使用简单工厂,您可以将创建对象的过程“隐藏”到工厂中,并在控制器中获取您需要的实例。所以你只是在工厂内部的开关盒中违反了 OCP,但我认为这可能是一个可以承受的权衡

      【讨论】:

      • 我会效仿你的。谢谢。
      • 需要修改工厂是对开放/封闭原则的典型违反:这意味着代码没有对修改关闭。事实上,switch/case 总是违反 OCP,这就是为什么它没有包含在 GoF 工厂方法设计模式中。设计模式基于多态性。
      • @jaco0646:设计模式不是绝对的规则,但重要的是让它们适应实际情况。在这种情况下,一个简单的工厂可能是一个很好的解决方案。正如我在帖子末尾指出的那样,根据应用程序的架构,操作可以实现抽象工厂或工厂方法
      • 简单工厂非常有用,尽管事实上它们违反了OCP,但这一点很好。然而,答案并没有通过展示一个简单的工厂然后声称它违反OCP来明确这一点。如果意图是说在这种情况下可以通过使用简单工厂来违反 OCP,那么这是一个有效的观点;但请注意 OCP 是作者关心的问题,这个答案可能会误导他认为一个简单的工厂符合 OCP,但事实并非如此。
      • @jaco0646 :OP 的主要关注点是不违反控制器中的 OCP,但是,从更一般的角度来看,您所说的是真的。我已经编辑了我的帖子以使事情更清楚
      【解决方案3】:

      我认为这主要取决于您的应用程序的范围。如今,在 PHP 世界中,人们对 if/else 语句非常生气 :)。但是,如果这适用于您的应用程序并且在您的上下文范围内看起来是合理的,我认为这很好。

      业务变化和业务变化并不总是很容易计划。您只能在这些更改出现时尝试使它们更容易。

      话虽如此,课程很便宜,我认为目前(以及将来)有单独的课程是非常合理的。如果对每种类型的报价的要求都扩大了,那么您将处于良好的地位,我认为您到目前为止还没有抽象到您的代码将难以理解。

      【讨论】:

      • 嗯..我认为你是对的..这个问题没有绝对正确的答案,你说的也是合乎逻辑的。我需要尽我所能坚持最佳实践,但有时由于应用程序上下文需要偏离轨道。
      • 这个答案太笼统了......不能将它复制并粘贴到 SO 上的每个 PHP 设计模式问题吗?
      • 通用与否,它是现实的。事实是,你不能对这类问题应用绝对真理。是的,有多种方法可以做事,但并非所有方法都适用于每种情况。而且我认为考虑这些问题是件好事,此外,要意识到在任何给定的情况下,一种方法可能会有所不同。
      • 另外,我在回答中承认他提出的解决方案是有效且合理的。但是,如果不知道他的应用程序的上下文,就不能说这是他的应用程序的绝对最佳解决方案。我相信这是一个值得考虑的重要想法。
      • 我认为这里的问题是问题本身。 “... 是个好主意吗?”是type of question to avoid,因为“每个答案都同样有效”。如果没有必要的背景来回答绝对真理,这个问题不适合 SO。 programmers 可能适合作为“软件架构和设计”问题。
      猜你喜欢
      • 1970-01-01
      • 2015-03-22
      • 1970-01-01
      • 1970-01-01
      • 2016-12-13
      • 2013-09-19
      • 2014-03-05
      • 2014-03-15
      • 2014-09-19
      相关资源
      最近更新 更多