【问题标题】:Php, Dependency Injection, how to replace objects (for testing)?Php,依赖注入,如何替换对象(用于测试)?
【发布时间】:2020-07-27 14:47:12
【问题描述】:

我们都知道简单的DI:减少耦合,并启用替换对象blahblahblah:

class testA
{
    public function m() {}
}

class testB
{
    public function m() {}
}

class Prj
{
    /**
     * @var testA
     */
    private $obj;

    /**
     *
     */
    public function __construct(testA $obj)
    {
         // $this->obj = new testA();
         $this->obj = $obj;
         $this->obj->m();
    }
}

$obj = new testA();
$project = new Prj($obj);

我知道$this->obj = new testA(); 引入了紧密耦合,如果我在 Prj::constructor 中完成的话。没错-“__construct(testA $obj)”不会?这也暗示需要“testA”。 以及如何更换它?这个:

$obj = new testB();
$project = new Prj($obj); // FAIL!

只是不会运行,因为它需要“testA”而不是“testB”。另一种骇人听闻的方式:

class testB extends testA
{
    public function m() {}
}

工作直到“testA”不是finaltestB 需要来自其他类的inherit...所以现在“紧密耦合”似乎并没有被我消除。 如果有人说“只需删除 testA 提示”:如果我使用 Java 或其他强类型语言进行开发怎么办?

【问题讨论】:

  • 听起来在测试中你想要像 test doubles 这样的东西 - PHPUnit test doubles
  • 不错,但这不是重点
  • 不完全确定您在问什么 - DI 更多的是在设计中构建灵活性,其他功能(作为最终功能)可以帮助您锁定某些设计决策。以数据库连接的常见用法为例——如果你在你的类中硬编码这个连接——你如何配置这个连接以使这个类使用一些特定的特性(另一个数据库、事务)?

标签: php dependency-injection


【解决方案1】:

没错 - 而“__construct(testA $obj)”不会?

不,testA 作为类型提示允许更多回旋余地:它允许您传递testA 的实例或其任何子类。正如您所展示的,您可以传递testB,只要testB extends testA。这意味着您有一种方法来改变行为。

如果您希望它更灵活(正如您正确指出的那样,虽然在__construct 中比new testA 更灵活,但它仍然有一些限制) ,你可以使用一个接口:

interface CanM {
    public function m();
}

class testA implements CanM {
    public function m() {}
}

...

    public function __construct(CanM $obj)

现在没有对你强加特定的类层次结构,你需要做的就是传递一个 implements CanM 的对象,这意味着它需要有一个符合指定函数签名的方法 m

【讨论】:

  • 其实很好,我遇到了很多“类似界面”的解决方案,但它需要整个设计重构
  • 好吧,要务实。只要您实际上还没有final化您的课程并且这只是一个假设的问题,extending/mocking 他们进行测试就可以了。确实应该从一开始就考虑接口;尽管也完全有可能在事后根据需要一点一点地更换它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2021-08-04
  • 2018-01-27
相关资源
最近更新 更多