【问题标题】:Is it bad practice to inject several arguments to the constructor?向构造函数注入多个参数是不好的做法吗?
【发布时间】:2014-11-01 03:23:13
【问题描述】:

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

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

我将在我的示例中使用 PRODUCT 对象。 对象/实体/域有一个类 class ProductService {}

这个类有一个服务提供者,它包含在提供者数组中并且也是自动加载的: ProductServiceServiceProvider

服务提供者实例化(制作)ProductRepository,这是一个接口。 该接口目前有一个名为EloquentProductRepository 的 MySQL(和一些 Eloquent)实现,ProductRepositoryServiceProvider 绑定了同样加载并在提供程序数组中的实现。

现在一个产品有许多不同的属性和与其他领域的关系,因为其他领域(或实体)需要完全分离并再次遵守上述原则(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 命名空间时这些名称可能会改变)?

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

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

总结:我知道这是一个非常可靠的设计,依赖项被注入并且它们确实是依赖项(即产品必须有税并且产品确实有重量等)并且它们可以很容易地与其他类感谢接口和服务提供者。现在感谢答案,我也知道在构造函数中注入这么多依赖项是可以的。

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

谢谢大家

【问题讨论】:

  • 如果你发现你在构造函数中注入了太多参数,那么可能是时候开始研究依赖注入容器了...看看 Laravel 的 IOC Container
  • @Mark Ba​​ker: ... 或者是时候开始认为类依赖太多了
  • 我不认为这是真的,如果一个类需要依赖特定的具体类,它应该是依赖注入的。所有写得好的 PHP 都应该遵循 IMO 的 SOLID 原则。使用 Laravel 的 IoC 会更容易。

标签: php laravel laravel-4 constructor service-provider


【解决方案1】:

一般来说,不,在大多数情况下,这不是一个坏习惯。但是在你的情况下,正如@zerkms 在 cmets 中所说,看起来你的类依赖于很多依赖项,你应该研究它,并考虑如何最小化依赖项,但如果你实际上正在使用他们应该在那里,我一点也不觉得有问题。

但是,您应该使用依赖注入容器 (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) 创建给定命名空间的实例,并创建它的构造函数参数和所有参数的构造函数参数等所需的实例。

【讨论】:

  • Laravel 自带 IoC 容器。你为什么提倡 Auryn?
  • 谢谢本。我想指出,这个类尤其具有这些依赖关系,它们一起构成了产品。我正在使用 Laravel IoC,并且我有注入实例的服务提供商类。我不喜欢在一个类中创建一个新实例,因为我认为这根本不是一个好习惯。该实例可能已经存在,因此一个新实例将不得不重建其属性等。我将添加有关该问题的更多信息,以便其他人可以更好地理解这种模式。同时感谢您回答我的问题。
  • @JosephSilber 我并不是 Lavarvel 的粉丝,因为据我所知,它通过静态方法创建和绑定。我总是被教导要基本上忘记 php 中的静态,并尽可能防止它。
  • @BenBeri - 你错了。 Laravel 通过静态方法不绑定任何东西。它只是附带提供对底层类的静态访问的代理类(他们称之为 Facades)。 IoC 容器本身通过静态方法绑定任何东西。
  • 静态全局访问。这是一堆垃圾。 Auryn 是一个出色的框架无关工具。
猜你喜欢
  • 2013-04-29
  • 2013-03-31
  • 2014-08-15
  • 2015-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多