具有存储库模式的Laravel中的SOLID原则

时间:2015-11-29 12:59:43

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

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

  1. 商业报价
  2. 私人报价
  3. 未来很有可能出现新类型的报价。每个报价都有不同的字段,业务逻辑,但它们共享许多常用功能。所以我创建了一个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中,它显然违反了开放/关闭原则..

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

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

3 个答案:

答案 0 :(得分:5)

如果您按照展示的方式继续前进,我建议您使用单个控制器作为报价,并使用工厂设计模式来创建$quotation个对象< / p>

例如,使用这样的简单工厂:

//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对象。

这也会使您的代码 DRY SOLID 保持不变,因为您不必重复if / else语句,以便在委派控制器的方法中创建对象对象创建对特定工厂类的责任

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

无论如何,从我对你的应用程序的看法来看,Simple工厂可能是一个很好的解决方案,因为你主要关注的是在变量类型的许多地方构建实例。因此,使用Simple工具,您可以“隐藏”创建对象到工厂的过程,并在控制器中获取所需的实例。所以你只是在交换机外壳工厂里面违反了OCP,但我认为这可能是一个可以权衡的交易

答案 1 :(得分:1)

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

企业变更,业务变更并不容易计划。您只能尝试在出现这些更改时更轻松。

话虽这么说,课程很便宜,我认为在这一点(以及将来)有单独的课程是非常合理的。如果每种报价的要求都有所扩大,那么你就会站稳脚跟,我认为你到目前为止还没有抽象,以至于你的代码难以理解。

答案 2 :(得分:0)

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

您应遵循以下原则:

“如果您有这样的条件,则应在低层抽象中进行而不是在高层中进行”

因此设计应为:

abstract class Quotation
{   
    abstract public function save(array $data);

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

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

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

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