工厂方法可能违反 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 和组件的过度依赖。该法的原则之一告诉我们以下内容:
在你的例子中,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 并且依赖于它。
我们在所有三个实例中都有相同的依赖关系。
引自此处: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 和组件的过度依赖。该法的原则之一告诉我们以下内容:
在你的例子中,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 并且依赖于它。 我们在所有三个实例中都有相同的依赖关系。