我有一个有趣的问题要提出;应该何时创建模型类/对象而不是为存储在数据库中的数据设置布尔标志?
例如,假设我有一个Person类,它具有President,Guard和PartTime的布尔标志。根据标志的值,对此类/模型的处理方式不同。因此,总统从Guard和PartTime(r)获得系统中的不同权限。
何时使用单表继承来表示此信息以及何时只会继续使用布尔标志?
我的直觉是使用STI将它们转换为不同的对象,因为这对我来说似乎更多。检查布尔值似乎在某种程度上是错误的,但我也可以看到它的位置。
更新澄清
让我使用另一个例子,因为上面的例子涉及太多案件。
我正在处理包含Pages的CMS应用程序,页面可以是公共页面,私有页面,共享页面,隐藏页面或默认页面(这意味着它是您在URL中未指定页面时获得的内容)。现在,我们有一个Page模型,一切都是一个布尔标志 - Public,Default,Shared。
我不相信这是处理此问题的最佳方法。特别是因为我们有规则来管理哪个页面可以是什么,即默认页面或共享页面必须是公共页面,而私有页面只是私有页面。
我同意下面的评论,人物角色的例子非常有意义。我不确定它的Page例子是什么。
为了使事情变得更复杂,只能有一个默认页面和一个共享页面。 STI可能允许我验证这一点,但我不确定,因为表中可能有许多默认和共享页面(只是与特定站点无关)。
注意:问题的上下文是Ruby on Rails应用程序,但适用于任何面向对象的语言。
答案 0 :(得分:4)
首先,让我们确定通常使用的单表继承。这是一种将多个事物的存储和行为相互结合的方法。坚持使用CMS,一个示例是posts
的表格,可以是Comment
或Article
。他们共享相似的数据和行为,但最终是不同的事情。无论某事是否是评论不是对象的状态,它都是一种身份。
但是,在您的示例中,无论页面是公共页面还是私有页面,是否共享或隐藏,都显示为页面的状态的一部分。虽然单表继承可能在技术上有效(假设所有子类都是互斥的),但它不适合。
状态应在一个或多个列中实施。表示某个双重状态的属性可以指定为布尔值; 是或否。如果页面总是为私有或公共,则可以将其建模为单个布尔列private
。如果它不是私人的,那就是公开的(或者相反)。
在某些情况下,您可能希望存储三个或更多互斥的不同状态。例如,一个页面可以是私有,公共或共享(我不知道是否是这种情况 - 让我们假装它是)。在这种情况下,布尔值将无济于事。您可以使用多个布尔标志,但正如您正确观察到的那样令人困惑。最简单的方法是将其建模为enumeration。或者当您缺少此功能时(如Rails的情况),只需使用具有特殊含义的字符串值并添加验证,以确保您使用的唯一值是private
,public
或{{}之一1}}。
有时,不同状态变量的某些组合无效。例如,页面可能是草稿或已批准(由布尔列shared
反映);它也是 public 或 private (也由布尔列反映)。我们可以决定在公开之前必须批准的页面。在这种情况下,我们声明其中一个状态无效。这应该通过模型的验证来体现。重要的是要意识到草案,公共页面不是从根本上不可能,它只是不可能,因为你决定不应该发生。
在创建模型时,请仔细区分反映现实世界中主题的实际属性和状态的属性,以及确定内容的业务规则应该是可能的,不应该是什么。第一个应建模为列,第二个建模为验证。
原始答案:
一个明显的区别是布尔标志允许approved
同时标记为总统和。如果您的模型应该允许这些情况,单表继承将不适合您。
另一方面,总统的Person
行为可能与普通人不同;一个人只能成为总统或后卫。在这种情况下,继承可能更合适。但我不认为你应该将“兼职”模型化为子类。无论如何,这是一个属性。
还有一个重要的第三个选项,即您可以将某个人的作业或角色与模型完全分开。一个人有一个(或多个?)工作,这些工作是兼职或不兼职。此模型的优点是您可以将人员的属性与其工作的属性分开。毕竟,人们会改变工作,但这并不能使他们成为一个与众不同的人。最终,在我看来,这是模拟你情况的最现实的方式。
答案 1 :(得分:0)
我不想为此使用标志,但也不要为此子类化Person。更确切地说,附加一个角色(或者如果你有一个既是总统又有卫士的角色,一组角色),并附有角色的子类来管理这些角色。
就个人而言,我既不是总统也不是卫兵,但我既是程序员又是音乐家,有时还担任过其他一些角色(事实上,我作为一名学生多年来同时兼任一名警卫前。)。
一个人有角色。
答案 2 :(得分:0)
我发现每当我想“嗯,我有这三种类型的行为并且它们看起来像子类,但需要在运行时更改”时,请查看策略或状态模式。它通常非常适合,并且通常也会在保持责任分开方面击败一个简单的布尔标志。
在您的情况下,此启发式方法会说您有一个具有AccessRights类型属性的Person,它决定是否可以执行某个操作。 Person可以访问此对象或委托适当的方法。之后,你有PresidentialRights,GuardRights和PartTimeRights实现这个AccessRights界面,你很高兴。
鉴于此,您无需在出现新类型的访问权限时更改人员类别,如果出现新类型的操作,您可能需要更改人员类别(取决于您是否委派以及如何委派)和为了添加新类型的AccessRights,您只需添加AccessRights的新实现。
答案 3 :(得分:0)
答案是它基本上是一个设计决定。没有一种先验正确的方法来设计架构。当您在它们之间定义类和关系时,您可以定义体系结构,同时还可以定义表示应用程序域的语言。
任何语言都由词汇组成(即人,总统,警卫等);语法(即您可以为词汇表实例指定的关系)和语义(即您在词汇和关系中指定的术语的含义)。
现在你可以以无限的方式获得相同的行为。任何人都会为同一个系统提出不同的架构,因为任何人都可能对问题有不同的思考方式。
尽管如此,在设计时还是应该考虑一些标准。 定义类时,您正在定义语言的“第一顺序”结构,当您为类定义属性时,您将描述第一个顺序结构的特征。
决定是否需要类或属性的最佳方法可能就是这个。 总统和警卫除了那些人之外还有不同的特点吗?如果是这种情况,并且它们具有许多不同的特征,那么你应该创建两个类(一个用于总统,一个用于Guard),这两个类都继承自Person。否则,您必须在Person类中折叠所有特征(属于人,属于总统和属于Guard的人),并将其有效性调整为另一个标志(类型)。这将是一个非常糟糕的设计
公开或不公开的页面的特征实际上描述了页面的状态。因此,将其建模为Page Class
的属性是非常合理的