了解IoC,DI和参考方法

Jon*_*han 8 php dependency-injection inversion-of-control

我正在学习依赖注入和控制反转,我想我已经开始理解它是如何工作的:

  • 对象不应该关注自己的依赖项的创建
  • 应该将依赖关系传递给对象(通过构造函数或setter方法)
  • DI容器可以完成创建具有所有必需依赖项的对象的工作

如果这一切都正确,我可以不再使用我在对象中称为"参考方法"的东西吗?

这是我的参考方法的意思.说我有两个家庭和家庭成员模特.我发现创建引用与该模型相关的对象的方法非常有用.在下面的示例中,在呼叫时$family->members(),我可以快速访问所有家庭成员.但是,这意味着我的family对象正在实例化family_member类......并且这不会违反IoC的规则吗?

如果family_member该类具有超出family该类范围的依赖性,该怎么办?这里的输入会很受欢迎!

<?php

    class family
    {
        public $id;

        public function members()
        {
            // Return an array of family_member objects
        }
    }

    class family_member
    {
        public $family_id;
        public $first_name;
        public $last_name;
        public $age;
    }
Run Code Online (Sandbox Code Playgroud)

ric*_*tie 5

免责声明:我自己就是在学习DI.拿一粒盐给出答案.

依赖注入仅涉及注入依赖关系.如果你的面向对象的设计结果Family有责任创建的实例对象Member通过各种手段,那么,有Family对象的创建Member,因为在这种情况下,Member不再被认为的依赖Family,而是一个责任.因此:

class Family
{
    /**
     * Constructor.
     * 
     * Since you have decided in your OO design phase that this
     * object should have the responsibility of creating members,
     * Member is no longer a dependency. MySQLi is, since you need
     * it to get the information to create the member. Inject it.
     *
     */
    public function __construct($id, MySQLi $mysqli)
    {
        $this->id = $id;
        $this->mysqli = $mysqli;
    }

    /**
     * Query the database for members data, instantiates them and
     * return them.
     *
     */
    public function getMembers()
    {
        // Do work using MySQLi
    }
}
Run Code Online (Sandbox Code Playgroud)

但是如果你想一想,Family真的应该有责任创造Member吗?更好的设计是拥有另一个对象,例如与其成员一起FamilyMapper创建Family.像这样:

class FamilyMapper
{
    /**
     * Constructor.
     * 
     * A better OO design, imho is using the DataMapper pattern.
     * The mapper's responsibility is instantiating Family,
     * which means it's going to have to connect to the database,
     * which makes MySQLi its dependency. So we inject it.
     *
     */
    public function __construct(MySQLi $mysqli)
    {
        $this->mysqli = $mysqli;
    }

    public function findByID($familyID)
    {
        // Query database for family and members data
        // Instantiate and return them
    }

}

class Family
{
    /**
     * Constructor.
     * 
     * Family is an object representing a Family and its members,
     * along with methods that *operate* on the data, so Member
     * in this OO design is a dependency. Inject it.
     *
     */
    public function __construct($id, MemberCollection $members)
    {
        $this->id;
        $this->members;
    }

    public function getMembers()
    {
        return $this->members;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此模式,您的域对象及其方法(可能包含业务逻辑)将与您的数据访问代码分离.这是依赖注入的好处 - 它会迫使你重新考虑你的OO设计,这样你最终会得到更干净的代码.

许多人认为使用依赖注入意味着不使用工厂等.这是错的!依赖注入仅涉及注入依赖关系.您也可以通过将依赖项注入工厂而不是让工厂实例化自己的依赖项来对工厂对象使用依赖项注入.

有用的链接:

  1. http://martinfowler.com/articles/injection.html
  2. 有没有人对依赖注入有一个很好的比喻?
  3. 如何解释5岁的依赖注射?

附加

再次,在这里拿下一粒盐.

另请注意,依赖注入依赖注入容器之间存在差异.第一个是注入依赖项的简单概念,而不是让对象自己创建它(这导致非常高的耦合).我们从上面的例子中看到了这一点.

后者是处理依赖注入的框架/库的术语,因此您不必手动注入.容器的责任是布线依赖性,因此您不必进行肮脏的工作.这个想法是你定义一个依赖注入配置,它告诉容器什么依赖Foo对象有什么,以及如何注入它们.容器读取文档并为您执行注射.这就是像Pimple,SimpleDIC这样的DIC库.

您可以将依赖注入容器与工厂进行比较,因为它们都是创建对象,其唯一的责任是创建对象.虽然工厂通常是专门的(即FamilyMemberFactory创建实例MemberInterface),但依赖注入容器更为通用.有人说使用依赖注入容器免除了您的需求,适用于工厂,但是你应该记住,这意味着你必须创建和维护依赖注入的配置文件,这可能是成千上万的XML/PHP线.

我希望这有帮助.

  • @Jonathan - 不幸的是,我不知道足以回答这个问题.但是如果我必须采用它,如果你使用Data Mapper模式,域对象就不应该知道持久性(通常是数据库)层.我们的想法是让域对象的职责集中在业务逻辑上,并让另一组类(数据映射器)处理持久性(选择,更新,删除方法).这使得您的域对象与数据源层分离,数据库中的更改仅涟漪到数据映射器.戈登说的也是. (2认同)