学说 ORM 和具有抽象类策略的工厂

Posted

技术标签:

【中文标题】学说 ORM 和具有抽象类策略的工厂【英文标题】:Doctrine ORM and factories with abstract classes strategy 【发布时间】:2015-07-27 21:37:43 【问题描述】:

所以我偶然发现了这个障碍,我必须创建一个抽象类和一个工厂来创建更具体的类的对象,这些类扩展抽象类并实现更具体的对象方法。

简单地说,我有一个 SocialMediaAbstract 类。扩展类是 Facebook、Instagram,它们实现了 SocialMediaInterface。 Facebook、Instagram 等都保存在数据库中,带有一个 id、一个名称和几个属性,这些属性都在扩展类中使用,因此是一个抽象类。

因为我希望能够从社交媒体对象中查询一些东西,并且每个社交媒体平台都有自己的 API,所以我制作了接口并创建了不同的类,因此它们都可以拥有自己的这些方法的实现.

现在,问题当然出在我的抽象类和 Doctrine 上。 Doctrine 在他们的website regarding inheritance 上这样说:

映射的超类不能是实体,它不可查询 [...]

现在,如果我有一个 SocialMediaFactory 并输入了一个 ID,我想获取相应的对象,例如 Facebook 或 Instagram 类。当我收集它们时,我不想确切知道它是哪个社交媒体。现在这是教义的问题,至少我认为是这样。

我是否忽略了什么,工厂模式仍然可能吗?或者我真的应该删除抽象类,并创建一个在 SocialMediaInterface 实现类的每个表中搜索的工厂,当应用程序变得更大时,这似乎非常低效且不可维护。

任何见解或指针都将不胜感激,因为我确信这个问题一定会更频繁地出现。我尝试在 *** 上进行谷歌搜索和搜索,但找不到任何相关问题或答案。

非常感谢您。

编辑: 我遇到了这个有趣的可能性:Class Table Inheritance。这意味着添加:

 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap("facebook" = "Facebook", "instagram" = "Instagram")

我的代码。我寄予厚望,但遗憾的是验证器给了我这个错误:

[学说\ORM\映射\映射异常] 不支持在映射的超类上定义继承信息 s 'Portal\SocialMedia\Entity\SocialMediaAbstract'。

不支持羞耻映射器超类。

编辑 2/结论: 我决定使用类表继承(就像下面建议的答案一样)。从类中删除抽象使得仍然可以使用我的工厂。

我现在使用一个具体类作为一个抽象类,但是感觉不对。我在 docblock 中记录了不应从此类实例化任何对象。

一点旁注:Doctrine 的实体管理器或多或少已经提供了工厂:

$socialMedia = $entityManager->find('Portal\SocialMedia\Entity\SocialMedia', 2);

这将返回一个 Instagram 对象。我仍然建议您在其上构建自己的工厂以便以后进行维护,因为社交媒体实体稍后可能会发生变化。

【问题讨论】:

如果需要class table inheritance,请删除@MappedSuperclass 注释。您不再需要它了,它用于不同的继承模式,请参阅我的答案以获得解释:) 【参考方案1】:

自从我使用教义以来已经过去了一段时间,但如果我没记错的话,doctrine's mapped super classes 是 Martin Fowler 的 concrete table inheritance pattern 的实现。

在上面提到的示例中,Player 是映射的超类,其属性分布到所有继承实体/模型。这里的重点是玩家不能被实例化,因此没有自己的 id。相反,每个继承模型都有自己的 id,它们都是相互独立的。

我认为您正在寻找的模式是single table inheritance 或class table inheritance(看看doctrine's inheritance types)。


Single table inheritance 在学说的继承类型“SINGLE_TABLE”中实现,其中所有实体都有一个表。它们共享完全相同的属性和相同的 id 池,这意味着您可以“输入”一个 id、获取对象并检查类型(Facebook、Instagram 等)。

不利的一面是,如果您在任何实体中获得了可能是NULL 的属性,如果其他实体没有此属性或不需要它,您可能会遇到问题。这意味着您必须将给定属性设置为其他实体中的虚拟值才能将它们保存到数据库表中。


Class table inheritance 通过将每个实体保存在自己的表中来克服这个问题,同时仍然能够共享 id 池,因为教义注意将公共属性保存在基类表中,而所有特定于实体保存在实体的表中。然后这些表由 id 连接,因此在学说中继承类型为“JOINED”。


结论:

如果类非常相似并且仅在函数定义或实现方面不同,但具有相同的属性,则使用single table inheritance。

如果类具有不同的属性,存储在单个表中会出现问题,请使用 class table inheritance。

如果类之间并不真正相关,但只共享少量的公共属性,则使用concrete table inheritance。但这也可以通过php's traits 来实现,在我看来,它比学说的映射超类更容易、更灵活地使用。在 PHP 特征中,您还可以使用学说的注解,因为 PHP 解释器会正确地将注解分配给您使用特征的类。

您应该仍然可以将 SocialMediaFactory 与单表或类表继​​承模式一起使用。

【讨论】:

我刚决定通过删除抽象部分来实现类表继承,当我回来时,你提出了这个建议。还是谢谢你。问题在于,以纯粹的方式,我将具体类用作抽象类。我想我必须在文件中记录它不应该被实例化。

以上是关于学说 ORM 和具有抽象类策略的工厂的主要内容,如果未能解决你的问题,请参考以下文章

设计原则工厂模式抽象工厂模式策略模式

Java中简单工厂模式,工厂模式,抽象工厂模式,策略模式和适配器模式学习

工厂/Builder,桥接/策略

绕过 ORM 类已经扩展了 DB 类并且您需要它从抽象类扩展的事实

设计模式之抽象工厂模式

如何告诉 Pex 不要存根具有具体实现的抽象类