我应该认真对待继承

时间:2013-06-19 05:57:20

标签: php oop inheritance table-per-type

我不知道我应该继承多么严重..

在我的应用程序中,我在大多数应用程序中都有客户,用户和供应商。

好吧,看起来很容易,但要成为细节的坚持者,我应该做到以下几点:

class Person {...}


class NaturalPerson extends Person {...}

class JuristicPerson extends Person {...}



class User extends NaturalPerson {...}

class Supplier extends JuristicPerson {...}

嗯,和客户有什么关系?在我看来,他们也可能是自然人和法人......

class JurCustomer extends JuristicPerson {...}

class NatCustomer extends NaturalPerson {...}
如果我想拥有所有客户,我需要选择两个相应的表...

基本上我更喜欢每个类型的模式...但根据上面的层次结构,我认为它可能会变得有点复杂。

嗯,这只是一个例子。在许多情况下,层次结构会更复杂......

2 个答案:

答案 0 :(得分:1)

这里要考虑的设计模式是:

  • ActiveRecord在数据库表或视图中包装行的对象,封装数据库访问权限,并在该数据上添加域逻辑。

这是经典的Table-per-Type方法。如果没有物体 - 关系阻抗不匹配,例如,该模式是适用的。当您的表与对象结构1:1匹配时。如果你有关系数据,你必须开始在ActiveRecord上填充ORM功能,这不是模式的用途。因此,当您拥有juristic_customer_tablejuristic_person_table等等时,请使用此功能。

许多应用中的现实是OO结构与DB结构不匹配,因为两者都解决了结构问题本质上的不同。此外,保持两者分离使得独立开发变得更加容易。因此,DataMapper通常是一个更好的选择。虽然选择更复杂。

  • SingleTableInheritance将类的继承层次结构表示为单个表,其中包含各个类的所有字段的列。

引用POEAA:此模式“将继承结构的所有类的所有字段映射到单个表中”。当您提取数据时,您通过Mapper(也处理CRUD)运行记录集,它知道要使用哪些数据实例化哪个类。为了便于使用,该表还可以保存对象的类名。这显然会将您的类的实现细节泄露到数据库中。


IMO这两种方法都是蠕虫病毒。你的继承层次结构对我来说也不太合理。请记住,继承意味着子类型是关于职责的超级类型。当您发现供应商的行为完全不像JuristicPerson时,您的子类型很快就会violate LSP。问问自己供应商是否需要公开法人的所有功能。如果没有,请考虑第三种方法:

根本不要使用继承,而是使用聚合。找到保存数据的基本类。然后制作封装该角色职责的小型Role对象,例如

$supplier = new Supplier(new Juristic(new Person(PersonGateway::findById(1))));

然后,您的供应商将掌握与供应商相关的所有逻辑。在与供应商交互时,您可能不需要Juristic或Person所拥有的任何内容。这应该都是供应商内部的。您的公共供应商界面将以这种方式小得多,并且可以更清晰地分离您的对象可以履行的职责。

这样您就不会创建is-a关系,而是具有-a或uses-a。我假设Juristic拥有某种与您的用例相关的业务逻辑。如果Juristic和Natural之间的唯一区别是语义,那么完全删除它。有关详细信息,请参阅my blog post on Modeling the Real World

答案 1 :(得分:1)

不必要的抽象层在不提供任何好处的情况下增加了复杂性。

您需要做的是确定从共享共同祖先的类中可以获得哪些实现优势。

我认为设计应用程序的最佳方式可能是从数据库模式开始,该模式准确地表示您正在处理的实体和关系。然后创建您的类以镜像该架构(每种类型的表)。

然后在考虑类似类的功能时,确定不同的类型是否会从共享基类中受益。例如,基于此设计技术,您最终会获得Customer和Supplier类。部分要求规定您需要一种获取信息以打印邮件标签的方法。如果您为获得此信息而进行的过程是相同的,那么在抽象类中进行设计是有意义的,然后两者都会扩展以实现该方法。

这有助于在设计应用时管理复制粘贴编码。