我正在开发一个相当复杂的物流管理系统,该系统将不断发展成为其他几个与 ERP 相关的模块。因此,我正在尝试尽可能多地采用 SRP 和开放/关闭原则,以便于扩展和基于域的管理。
因此,我决定使用 Laravel 和以下模式(不确定是否有名称):
我将使用 PRODUCT 对象作为示例。
一个对象/实体/域有一个类
类 ProductService {}
这个类有一个 Service Provider,它包含在 providers 数组中,也是自动加载的:
ProductServiceServiceProvider
服务提供商实例化(制作)作为接口(interface)的 ProductRepository。
该接口(interface)当前有一个名为 EloquentProductRepository 实现的 MySQL(和一些 Eloquent)和一个 ProductRepositoryServiceProvider 绑定(bind)实现,该实现也已加载并位于提供程序数组中。
现在一个产品有许多不同的属性和与其他领域的关系,因为其他领域(或实体)需要完全分离并再次遵守上述原则(SRP 等。)我决定也有相同的结构对他们来说,就像我对产品所做的那样......我知道有些人可能认为这太多了,但我们需要让系统具有很强的可扩展性,老实说,我喜欢有条理并有一个统一的模式(它不需要多了很多时间,以后也省了很多。
我的问题是这样的。 ProductService 处理 Product 的所有业务逻辑并使“Product”成为现实,将在通过构造函数创建其实例时注入(inject)多个依赖项。
目前是这样的:
namespace Ecommerce\Services\Product;
use Ecommerce\Repositories\Product\ProductRepository;
use Ecommerce\Services\ShopEntity\ShopEntityDescriptionService;
use Content\Services\Entity\EntitySeoService;
use Content\Services\Entity\EntitySlugService;
use Ecommerce\Services\Tax\TaxService;
use Ecommerce\Services\Product\ProductAttributeService;
use Ecommerce\Services\Product\ProductCustomAttributeService;
use Ecommerce\Services\Product\ProductVolumeDiscountService;
use Ecommerce\Services\Product\ProductWeightAttributeService;
use Ecommerce\Services\Product\ProductDimensionAttributeService;
/**
* Class ProductService
* @package Ecommerce\Services\Product
*/
class ProductService {
/**
* @var ProductRepository
*/
protected $productRepo;
/**
* @var ShopEntityDescriptionService
*/
protected $entityDescription;
/**
* @var EntitySeoService
*/
protected $entitySeo;
/**
* @var EntitySlugService
*/
protected $entitySlug;
/**
* @var TaxService
*/
protected $tax;
/**
* @var ProductAttributeService
*/
protected $attribute;
/**
* @var ProductCustomAttributeService
*/
protected $customAttribute;
/**
* @var ProductVolumeDiscountService
*/
protected $volumeDiscount;
/**
* @var ProductDimensionAttributeService
*/
protected $dimension;
/**
* @var ProductWeightAttributeService
*/
protected $weight;
/**
* @var int
*/
protected $entityType = 3;
public function __construct(ProductRepository $productRepo, ShopEntityDescriptionService $entityDescription, EntitySeoService $entitySeo, EntitySlugService $entitySlug, TaxService $tax, ProductAttributeService $attribute, ProductCustomAttributeService $customAttribute, ProductVolumeDiscountService $volumeDiscount, ProductDimensionAttributeService $dimension, ProductWeightAttributeService $weight)
{
$this->productRepo = $productRepo;
$this->entityDescription = $entityDescription;
$this->entitySeo = $entitySeo;
$this->entitySlug = $entitySlug;
$this->tax = $tax;
$this->attribute = $attribute;
$this->customAttribute = $customAttribute;
$this->volumeDiscount = $volumeDiscount;
$this->dimension = $dimension;
$this->weight = $weight;
}
`
在 PHP 中将尽可能多的参数传递给构造函数是否是一种不好的做法(请忽略服务的长名称,因为在确定 ERP namespace 后这些名称可能会发生变化)?
正如下面 Ben 的回答,在这种情况下不是。我的问题与 OOP 无关,而是与性能等有关。原因是这个特定的类 ProductService 是 Web 开发人员对 Controller 所做的事情,即他们可能(并且违反原则)在一个 ProductController 中添加所有数据库关系处理存储库服务(数据库等)并附加关系,然后突然变成您的业务逻辑。
在我的应用程序中(我看到大多数应用程序都是这样),Web 层只是另一层。 MVC 负责 web 层,有时也负责其他 Apis,但除了与我的 MVC 中的 View 和 JS 框架相关的逻辑外,我不会有任何逻辑。所有这些都在我的软件中。
结论:我知道这是一个非常 SOLID 的设计,注入(inject)了依赖项,它们确实是依赖项(即产品必须有税,产品确实有重量等),并且它们可以很容易地与其他产品交换类归功于接口(interface)和 ServiceProviders。现在多亏了答案,我也知道在构造函数中注入(inject)这么多依赖项是可以的。
我最终会写一篇关于我使用的设计模式以及为什么我在不同场景中使用它们的文章,如果您对此感兴趣,请关注我。
谢谢大家
最佳答案
一般来说,不,在大多数情况下,这不是一个坏习惯。但是在你的情况下,正如@zerkms 的评论中所说,看起来你的类依赖于很多依赖项,你应该研究它,并考虑如何最小化依赖项,但如果你实际使用他们,他们应该在那里,我根本看不出有什么问题。
但是,您应该使用依赖注入(inject)容器 (DIC)。
依赖注入(inject)容器,基本上是一种工具,它通过您提供的命名空间创建类,并创建包含所有依赖项的实例。您还可以共享对象,因此它不会在创建依赖项时创建它的新实例。
我建议你使用 Auryn DIC
用法:
$provider = new Provider();
$class = $provider->make("My\\App\MyClass");
这里发生的事情是这样的:
namespace My\App;
use Dependencies\DependencyOne,
Dependencies\DependencyTwo,
Dependencies\DependencyThree;
class MyClass {
public function __construct(DependencyOne $one, Dependency $two, DependencyThree $three) {
// .....
}
}
基本上,Provider#make(namespace) 创建给定命名空间的实例,并创建其构造函数参数和所有参数的构造函数参数等所需的实例。
关于php - 向构造函数注入(inject)多个参数是不好的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25715252/
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这