gpt4 book ai didi

php - 为什么我们需要构造函数?

转载 作者:可可西里 更新时间:2023-11-01 00:57:59 25 4
gpt4 key购买 nike

假设:(1) 有一个带有构造函数的类,其方法使用注入(inject)对象,如:

class SomeClass
{
protected $object1;
protected $object2;

public function __construct(
Object1Interface $object1,
Object2Interface $object2
) {
$this->object1 = $object1;
$this->object2 = $object2;
}

// methods that use Object1 and Object2 classes by $this->object1 and $this->object2.
}

和 (2) 是同一个类,没有构造函数,但是类方法接受 Object1Object2 作为依赖项,如下所示:

class SomeClass
{
public function doStuff1(Object1Interface $object1)
{// do the stuff}

public function doStuff2(Object2Interface $object2)
{// do the stuff}
}

互联网上有许多提倡第一种变体的例子。

但是它们之间有什么区别呢?

最佳答案

它们是不同的,并没有真正提倡一个比另一个。

(1) 被广泛称为构造函数依赖注入(inject)。它被提倡为比 setter 依赖注入(inject)更好的首选形式,而不是 (2)。这些依赖项对消费者是“隐藏”的,并且在对象生命周期内通常不会更改。

考虑抽象的 http 客户端:

$httpClient = new HttpClient(new CurlAdapter());
// or
$httpClient = new HttpClient(new SocketAdapter());

切换适配器不会影响客户端的使用方式:

$response = $httpClient->get($url);

构造函数 DI 被提倡优于 setter,因为它强制注入(inject)依赖项。此外,setter 通常允许在对象生命周期内更改依赖关系,从而改变其行为并为棘手且难以调试的错误打开可能性。

$dataContainer = $serviceLocator->get('SomeDataContainer');
$dataContainer->setStorage(new ArrayStorage());
$dataContainer->set('a','b');
$dataContainer->get('a'); // => 'b'
// called somewhere else
{
$serviceLocator
->get('SomeDataContainer')
->setStorage(new RedisStorage());
}
$dataContainer->get('a'); // => 'foobar' WAT

(2) 用于不同的用例,通常不与 DI 重叠。以这种方式传递依赖关系有多种原因。它们可能是交互界面的一部分,在对象生命周期中经常更改或在调用时决定。依赖项在概念上可能不属于对象,但对于该特定操作仍然需要。重要的是,它们不应存储在对象中并以可能导致副作用的方式使用!

class SomeClass
{
protected $dep;

public function doSomething(DepInterface $dep)
{
// do the stuff
// store dep for later
$this->dep = $dep;
// This is similar to issue with setters but much worse.
// Side effect is not obvious
}

public function doSomethingElse()
{
$this->dep->increment();
}
}

for ($i = 0; $i < 100; $i++) {
$foo->doSomething($bar);
if (rand(0, 100) == $i) {
// represents conditions out of direct control.
// such as conditional or timing errors, alternative flows,
// unanticipated code changes
$foo->doSomethng($baz);
}
$foo->doSomethingElse();
}
// what will be the result?

考虑这个 DDD 示例来解决一些不切实际的问题:

class Bus
{
public function board(Passenger $passenger, TicketingService $tservice)
{
// @todo check bus have seats
// throws exception if ticket is invalid
$tservice->punchPassengerTicket($this, $passenger);
$this->passengers[] = $passenger;
}
}

if ($bus->isIntercity()) {
$ticketingService = new BookingService();
} else {
$ticketingService = new PrepaidCityTransportCardsService();
}
$bus->board($passenger, $ticketingService);

在这个例子中,我们明确强制登车巴士需要门票,并且在上车时打票。但是,如果我们要在 Bus 实例化时注入(inject) TicketingService,即使在根本没有上车的情况下,我们也会得到更复杂的依赖图。在这种情况下,通过服务来执行操作但从不存储它会大大降低复杂性并显着提高可测试性。

这种技术在领域驱动设计上下文中称为双重分派(dispatch)(不要与 Double Dispatch 混淆)并被广泛使用。
您可能认为我们可以在登机前检查机票并完全消除对票务服务的依赖。好吧,这完全是另一个话题。

关于php - 为什么我们需要构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35817123/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com