在维护SOLID原则的同时,我对使用Controller with Repository Pattern感到困惑。考虑一下,我有两种类型的报价
未来很有可能出现新类型的报价。每个报价都有不同的字段,业务逻辑,但它们共享许多常用功能。所以我创建了一个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违规或我的设计错误?欢迎任何建议,设计变更提示,资源。
注意:除了仅保存之外,我的报价控制器还有许多其他功能。
答案 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所著的“重构:改进现有代码的设计”一书。