为什么不“在对象构造函数中实例化一个新对象”?

2022-08-30 19:59:25

我回答了一个问题(链接),我在另一个类的构造函数中使用了新对象的创建,这里的例子是:

class Person {
  public $mother_language;

  function __construct(){ // just to initialize $mother_language
    $this->mother_language = new Language('English');
}

我从用户“Matija”(他的个人资料)那里得到了评论,他写道:你永远不应该在对象构造器内部实例化一个新对象,依赖关系应该从外部推送,所以任何使用这个类的人都知道这个类依赖于什么!

总的来说,我可以同意这一点,我理解他的观点。

但是,我曾经经常这样做,例如:

  • 由于私有属性,其他类为我提供了可以解决的功能,而不是重复代码,例如,我可以创建对象的列表(类实现接口),并且该类将用于另一个类,该类具有这样的对象列表,ArrayAccess
  • 一些类使用例如对象,DateTime
  • 如果 I(或 autoload)依赖类,则错误应该没有问题,include
  • 因为依赖对象可以是非常大的数字,所以将它们全部传递给类构造函数可能很长并且不清楚,例如

    $color = new TColor('red'); // do we really need these lines?
    $vin_number = new TVinNumber('xxx');
    $production_date = new TDate(...);
    ...
    $my_car = new TCar($color, $vin_number, $production_date, ...............);
    
  • 因为我“出生在”帕斯卡,然后在德尔斐,我有一些习惯。在Delphi(以及FreePascal作为其竞争对手)中,这种做法非常频繁。例如,有一个类处理字符串数组,并且为了存储它们,它不使用s,而是使用另一个类,它提供了一些有用的方法,而只是某种接口。该对象是私有声明的,除了 的 getter 和 setter 之外,没有来自外部的访问权限。TStringsarrayTListTStringsTListTStrings

  • (不重要,但有一些原因)通常我是使用我的课程的人。

请解释一下,避免在构造函数中创建对象真的很重要吗?

我已经阅读了这个讨论,但仍然不清楚。


答案 1

是的,确实如此。然后,您将清楚对象需要什么才能被构造。传入的大量依赖对象是一种代码气味,也许您的类执行了太多操作,应该在多个较小的类中分解。

传入依赖对象的主要优点是,如果要测试代码。在你的例子中,我不能使用假的语言类。我必须使用实际的类来测试Person。我现在无法控制语言的行为方式,以确保 Person 正常工作。

这篇文章有助于解释为什么这是一件坏事以及它导致的潜在问题。http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/

更新

除了测试之外,传入依赖对象还使您的代码更加明确、灵活和可扩展。引用我链接到的博客文章:

当协作者构造与初始化混合在一起时,它表明只有一种方法可以配置类,从而关闭了本来可能可用的重用机会。

在您的示例中,您只能创建以“英语”作为语言的人员。但是,当您想要创建一个说“法语”的人时,该怎么办?我无法定义。

至于创建对象并传入它们,这就是模式 http://www.oodesign.com/factory-pattern.html 的全部目的。它将创建依赖项并为您注入它们。因此,您将能够向它询问将以所需方式初始化的对象。Person 对象不必决定它需要什么。Factory


答案 2

原来的评论在我看来很愚蠢。

没有理由害怕管理资源的类。实际上,类提供的封装非常适合此任务。

如果你把自己限制在只从外部推动资源,那么如何管理这些资源呢?意大利面条程序代码?哎呀!

尽量避免相信你在互联网上读到的一切。


推荐