工厂方法可能违反 Demeter 法则?

A factory method may violate the Law Of Demeter?

引自此处:https://en.wikipedia.org/wiki/Law_of_Demeter

More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:[2]

  • O itself

  • m's parameters

  • Any objects created/instantiated within m

  • O's direct component objects

  • A global variable, accessible by O, in the scope of m

In particular, an object should avoid invoking methods of a member object returned by another method

详情如下:

class O
{
    private $c;
    public function m($obj1)
    {
        $this->a(); // OK
        $obj1->a(); // OK
        (new C())->a(); // OK
        $c->a(); // OK
        $a = function() { };
        $a(); // OK
    }

    private function a() {}
}

现在第三定律值得怀疑。所以我新创建了一个对象。但如果我不是:

(new C())->a();

我愿意:

$this->factory->createC()->a();

它仍然有效吗?一个常规的 class 被实例化了,只是不是由 new 而是一个工厂。但是,嘿!定律说:

In particular, an object should avoid invoking methods of a member object returned by another method

按照这个规则,工厂方法失败了!怎么办?真的失败了吗?

我不这么认为。

尤其是这个:

Any objects created/instantiated within m

我也会将其应用于工厂。即使严格来说对象的构造函数是在工厂中调用的,对象仍然是由 for m 构造的。我会将工厂解释为一种特殊的构造函数,并且忽略了你在那里没有看到 new 关键字的事实。

鉴于工厂在软件设计中扮演的各种重要角色(控制反转是其中之一),我认为它们太有价值了,不能放弃。最好改变你对这条定律的解释,或者什么是构造函数,并在你需要的时候使用这些工厂。

不应该太严格地遵循源于得墨忒耳法则的规则。有必要了解这项法律的意义和目的。 遵守 Demeter 法则有助于我们避免代码对外部 classes 和组件的过度依赖。该法的原则之一告诉我们以下内容:

Each unit should have only limited knowledge about other units: only units "closely" related to the current unit

在你的例子中,O class 在任何情况下都知道 C class。使用工厂不会影响这个事实。不知何故,另一个 O class 依赖于 C class 并且这种依赖是不可避免的。这意味着依赖性并不过分。实际上Cclass和Oclass之间的依赖是"closely"相关单元之间的依赖,所以使用工厂并不违反得墨忒耳法则[=14] =]

举个例子,让我们想象一下下面的代码实例:

class O
{

   public function m()
   {
      $c = new C();
      $c->a();
   }
}

如您所见,O class 知道 C class 并且依赖于它。此代码并未违反得墨忒耳法则。如果您将此示例修改为:

class O
{

   protected function build()
   {
      return new C();
   }

   public function m()
   {
      $c = $this->build();
      $c->a();
   }
}

class O 还是会知道 C class 并依赖它,此代码不会违反 Demeter 定律。实际上,我们把制作对象的责任委托给了工厂方法。如果您将此示例修改为:

class Factory
{
   public function build()
   {
      return new C();
   }
}

class O
{

   /**
    * @var Factory
    */
   protected $factory;

   public function m()
   {
      $c = $this->factory->build();
      $c->a();
   }
}

我们把制作对象的责任委托给了工厂对象,但是这个事实违反了得墨忒耳法则,因为Oclass和Cclass之间的依赖关系是不会改变的。与前面的实例一样,O class 知道 C class 并且依赖于它。 我们在所有三个实例中都有相同的依赖关系。