SOLID Principle In Laravel with Repository Pattern

SOLID Principle In Laravel with Repository Pattern

我对在维护 SOLID 原则的同时使用 Controller with Repository Pattern 感到有些困惑。考虑一下,我有两种类型的 Quotations

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

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

报价界面

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

}

实现接口的引用class

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 或我的设计是个好主意只是错了吗?欢迎任何建议、设计更改提示和资源。

注意:我的 Quotation Controller 除了仅保存外还有许多其他功能。

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

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

话虽这么说,类 很便宜,我认为现在(以及将来)单独使用 类 是非常合理的。如果对每种类型的报价单的要求都扩大了,那么您的立足点就会很好,而且我认为您目前还没有抽象到代码难以理解的程度。

如果你按照你所展示的方式前进,我建议你为你的报价单使用一个控制器,并使用 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);
}

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

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

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

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

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

您应该遵循以下原则:

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

所以设计应该是:

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.");
        }
    }
}

上述重构称为“用多态替换conditional/type代码”。

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

要了解更多信息,您应该阅读 Martin Fowler 的一本名为“重构:改进现有代码的设计”的书。