向构造函数注入几个参数是不是一种糟糕的做法


Is it bad practice to inject several arguments to the constructor?

我正在开发一个相当复杂的物流管理系统,该系统将不断发展成为其他几个与ERP相关的模块。因此,我正在努力制定尽可能多的SRP和开放/关闭原则,以便于扩展和基于域的管理。

因此,我决定使用Laravel和以下模式(不确定这是否有名字):

我将使用PRODUCT对象作为示例。对象/实体/域具有类class ProductService {}

此类具有一个服务提供程序,该服务提供程序包含在提供程序阵列中,并且也是自动加载的:ProductServiceServiceProvider

服务提供商实例化(制作)作为接口的ProductRepository。该接口目前有一个名为EloquentProductRepository实现的MySQL(和一些Eloquent),一个ProductRepositoryServiceProvider绑定了该实现,该实现也已加载并位于providers数组中。

现在,一个产品与其他领域有许多不同的属性和关系,因为其他领域(或实体)需要完全分离,并再次遵守上述原则(SRP等)。。。我知道有些人可能会认为这太多了,但我们需要让这个系统非常可扩展,老实说,我喜欢有组织,有一个统一的模式(这不会花太多时间,以后会节省我很多时间)。

我的问题是。ProductService处理产品的所有业务逻辑,并使"产品"成为它的样子,它将在通过构造函数创建它的实例时注入几个依赖项。

这就是目前的情况:

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名称空间后,这些名称可能会发生变化)?

正如本在下面回答的那样,在这种情况下不是。我的问题与OOP无关,更多的是与性能等有关。原因是,这个特定的类ProductService是web开发人员对控制器所做的事情,即他们可能会(违背原则)将所有DB关系添加到一个ProductController中,该ProductController处理存储库服务(数据库等)并附加关系,然后它突然成为您的业务逻辑。

在我的应用程序中(我看到的大多数应用程序都是这样),web层只是另一层。MVC负责web层,有时也负责其他Api,但除了与MVC中的视图和JS框架相关外,我不会有任何逻辑。所有这些都在我的软件中。

总之:我知道这是一个非常坚固的设计,依赖项被注入,它们实际上是依赖项(即产品必须有税,产品确实有重量等),由于接口和ServiceProviders,它们可以很容易地与其他类交换。现在多亏了这些答案,我也知道在构造函数中注入这么多依赖项是可以的。

我最终会写一篇关于我使用的设计模式的文章,以及我为什么在不同的场景中使用它们,所以如果你对此感兴趣,请关注我。

感谢大家

一般来说,不,在大多数情况下,这不是一个糟糕的做法。但在你的情况下,正如@zerkms在评论中所说,你的类似乎依赖于很多依赖项,你应该研究一下,并思考如何最大限度地减少依赖项,但如果你真的在使用它们,并且它们应该在那里,我看不出有任何问题。

但是,您应该使用依赖注入容器(DIC)。

依赖项注入容器基本上是一种工具,它通过您提供的命名空间创建类,并创建包括所有依赖项的实例。您还可以共享对象,这样在创建依赖关系时就不会创建它的新实例。

我建议你使用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)创建给定名称空间的一个实例,并创建它的consturtor参数和所有参数的构造函数参数等所需的实例

相关文章: