两个相互依赖的类的构造函数注入

时间:2016-01-19 21:58:00

标签: php oop

用户填写表单并提交。根据输入,对象Organization是水合的。我想将数据库与实际对象的通信分开。

我想创建一个OrganizationMapper来保存数据库通信的方法(保存,删除...)。组织类将通过构造函数获得OrganizationMapper

但是,通过这些类定义,我无法实例化类,因为它们相互依赖。

我如何将数据库通信与Organization分开并将其放入OrganizationMapper

class Organization
{
    protected $id;
    protected $name;
    ... other properties ...
    public function __construct(OrganizationMapper $mapper)
    {
        $this->mapper = $mapper;
    }
    public function getId() {...}
    public function setId($id) {...}
    ... other methods ...
    public function saveToDb()
    {
        $this->mapper->save($this);
    }

OrganizationMapper

class OrganizationMapper
{
     public function __construct(Organization $organization)
     {
         $this->organization = $organization
     }

     ... other methods

     public function save($organization)
     {... the code to use the methods of Organization class to save the data to the database...}
}

5 个答案:

答案 0 :(得分:3)

这就是为什么循环依赖通常被认为是坏事。

除了开玩笑,在我看来,你实际上并不需要OrganizationMapper类中的构造函数依赖项。从它的外观来看,您将要保留的Organization实例作为参数传递到映射器的save()方法中,并且不应该需要该实例该类中的属性$this->organization

一般情况下,我会尝试保留OrganizationMapper 无状态。尽量避免将Organization实例存储为实例属性(尤其是,如果您实际使用相同的映射器实例来保存多个Organization)。就像您使用save()方法一样,并将Organization对象作为方法参数传递。

另外,我不会将Organization类与映射器关联起来。有人可能会认为这违反了Single Responsibility Principle,因为它不属于班级'坚持自己的责任。您可以将此逻辑移动到调用代码,让Organization类完全不知道映射器 (这很好,因为您完全取消了循环两个类之间的依赖关系):

class Organization
{
    protected $id;
    protected $name;
    // <other properties here>

    // <getters and setters here>
}

class OrganizationMapper
{
    public function save(Organization $organization)
    {
        // save $organization to DB, somehow
    }
}

$organization = new Organization();
$organization->setName('Foobar International Inc.');

$mapper = new OrganizationMapper();
$mapper->save($organization);

答案 1 :(得分:2)

要找到更好的方法来分离这两个问题,请考虑两个目标的目的:

  • 组织可让您访问组织的所有信息
  • 您的 OrganizationMapper 用于将 Organization 对象保存到数据库。

当你这样想的时候,会有一些问题出现:

  • 为什么 Organization 需要 saveToDb()方法?保存它不是它的工作吗?
  • OrganizationMapper 的实例应该能够在数据库中保存任何 Organization ,那么为什么要将它传递两次呢? (一次在构造函数中,一次在 save($ organization)方法中)。在这种情况下 - 如果您将不同的组织传递给构造函数而不是save方法,会发生什么?
  • 在您当前的示例中,您将如何从数据库加载组织?

作为替代方案,我建议完全从 Organization 中删除 saveToDb(),因为它不是将自己保存到数据库的组织工作。另外,我会从 OrganizationMapper 中删除当前的构造函数。在它的当前设计中,没有理由将组织传递给构造函数。 另外,我会将OrganizationMapper重命名为OrganizationRepository或OrganizationService。该类的主要目的不是将SQL映射到对象,而是从/向DB检索/保存组织。 (另外,在OOP中,类应该只遵循单一的责任模式,因此SQL和Objects之间的部分映射可能会在专门的类中发生)

作为旁注:一般来说,提供多种方法来完成同样的事情(例如,保存组织)并不是一个好主意。这可能只会导致一段时间的不一致(考虑到将来会添加一些验证逻辑,但可能会忘记在第二位添加它)。

我希望这可以帮到你:)

答案 2 :(得分:1)

免责声明:我在此帖子中将您的Organization类型命名为OrganizationEntity

非常简单,反之亦然。

OrganisationMapper获取OrganisationEntity个对象并通过您可以选择的方式将其保留在您想要的任意位置。

对于你的问题:

saveToDb()方法从OrganisationEntity移至OrganisationMapper,并将其传递给要保存的对象。

答案 3 :(得分:1)

我不知道为什么Mapper会对数据库执行任何操作? Mapper听起来像将实体(组织)转换为可以作为DB操作输入的东西,即。 Query Object。 您应该将班级重命名为DAORepository。这会是更好的名字。

恕我直言,最好的想法是:

  1. 组织作为保存域逻辑的对象
  2. OrganizationMapper应将您的域对象转换为某种查询对象
  3. OrganizationDao应该将Organization作为输入参数并使用OrganizationMapper进行转换并对DB进行操作。
  4. 顺便说一下,为什么你没有使用像Doctrine这样的ORM呢?它会让你的生活更轻松:)

答案 4 :(得分:0)

你不能在php中这样做。想象一下,如果它是posibble。然后,组织实例将拥有一个属性OrganizationMapper,该属性具有属性Organization。因此,类实例的属性的属性将是实例本身!它只能用于像c ++这样的指针语言。所以,我在这里只看到两个解决方案:

  • 将课程放在一起
  • 有一个链接(可能有一个类调用另一个,而第二个不调用。)