何时创建类而不是设置布尔标志?

8
我有一个有趣的问题要提出:什么时候应该创建一个模型类/对象,而不是在数据库中设置一个布尔标志呢?
例如,假设我有一个Person类,其中包含总统、警卫和兼职等布尔标志。这个类/模型根据标志的值而被不同地处理。所以总统在系统中得到不同于警卫和兼职人员的特权。
什么时候应该使用单表继承来表示这些信息,什么时候应该继续使用布尔标志呢?
我的直觉告诉我,使用STI将它们转换为不同的对象更符合面向对象的思想。检查布尔值似乎有些不对劲,但我也能看到它的用处。
更新说明:
让我举个例子,因为上面那个例子涉及的情况太多了。
我正在开发一个CMS应用程序,其中包含页面,页面可以是公共的、私有的、共享的、隐藏的或默认的(意思是当你在url中没有指定页面时会显示默认页面)。现在,我们有一个页面模型,一切都是布尔标志——公共、默认、共享。
我不确定这是处理这个问题的最佳方法。特别是因为我们有规则来管理哪个页面可以是什么,例如,默认页面或共享页面必须是公共页面,而私有页面只是私有的。
我同意下面的评论说Person示例中的角色非常有意义。但对于页面示例,我不确定是否也是如此。
为了让事情更加复杂,只能有一个默认页面和一个共享页面。STI可以让我验证这一点,但我不确定,因为表中可能有许多默认页面和共享页面(只是没有与特定站点相关联)。
注:问题的上下文是Ruby on Rails应用程序,但适用于任何面向对象的语言。
4个回答

5
首先,让我们确定单表继承通常用于什么。它是一种将多个相似的事物的存储和行为结合在一起的方式。以CMS为例,一个示例是具有“posts”表的表,它可以是“Comment”或“Article”。它们共享相似的数据和行为,但最终是不同的东西。某些内容是否是评论不是对象的状态,而是身份。
然而,在您的示例中,页面是否为公共、私有、共享或隐藏似乎是页面的状态的一部分。虽然单表继承可能在技术上可行(前提是所有子类是互斥的),但并不适合。
状态应该在一个或多个中实现。表示某种双重状态的属性可以指定为布尔值;。如果页面始终是私有或公共的,则可以将其建模为单个布尔列private。如果它不是私有的,则是公共的(或反之亦然)。
在某些情况下,您可能希望存储三个或更多互斥的不同状态。例如,页面可以是“私有的,公共的或共享的”(我不知道是否是这种情况--让我们假装是这样)。在这种情况下,布尔值将无法帮助。您可以使用多个布尔标志,但正如您正确观察到的那样,这非常令人困惑。最简单的方法是将其建模为枚举。或者当您缺乏此功能时(如Rails),只需使用具有特殊含义的字符串值,并添加一个验证,以确保您使用的唯一值是“private”,“public”或“shared”之一。
有时不同状态变量的某些组合是无效的。例如,页面可能是“草稿”或“已批准”(由布尔列“approved”反映);它也可以是“公共”或“私人”(也由布尔列反映)。我们可以决定在将页面发布之前必须先批准该页面。在这种情况下,我们声明其中一个状态无效。这应该通过您的模型验证来反映。重要的是要意识到,“草稿”,“公共”页面并不是从根本上不可能的,只是因为您决定不应该发生而不可能。
创建模型时,请仔细区分反映实际属性和状态的属性与确定什么是可能和什么不可能的业务规则。前者应建模为列,后者应建模为验证。

原始回答:

一个明显的区别是布尔标志允许将Person标记为总统警卫。如果您的模型应允许这些情况,则单表继承将无法为您工作。

另一方面,也许作为总统的Person 行为与普通人不同;并且一个人只能是总统警卫。在这种情况下,继承可能更适合。但我认为你不应该将“兼职”建模为子类。无论如何,那都是一个属性。

还有一个重要的第三个选项,就是完全将人的工作角色与模型分开。一个有一个(或多个?)工作,它们是全职还是兼职。这种模型的优点是将人的属性与他们的工作属性分开。毕竟,人们会换工作,但这并不意味着他们真正成为了不同的人。最终,这对我来说似乎是模拟您的情况的最现实的方法。


是的,我同意。不幸的是,我的例子并不适用于我所面临的具体情况。我已经更新了我的帖子,包括我正在研究的具体问题。 - Chris Johnston

0

我更倾向于不使用标志(flag)来实现这个功能,也不想为此而派生(Person)类。相反,应该附加一个角色(Role),如果有人既是总统又是警卫,可以使用一组角色(Role)来管理特权。

就我个人而言,我既不是总统也不是警卫,但我既是程序员又是音乐家,并且有时还有其他角色(事实上,很多年前我曾同时担任学生和警卫)。

一个人(Person)拥有一个角色(Role)。


使用了一个不好的例子,请查看我上面的更新。虽然完全同意你的观点,但我不确定它是否适用于实际情况。我需要再考虑一下。 - Chris Johnston

0

我发现每当我想“嗯,我有这三种行为类型,它们看起来像子类,但需要在运行时更改”时,就使用策略或状态模式。它通常非常适合,并且通常还优于简单的布尔标志,可以将责任分开。

在你的情况下,这个启发式方法会说你有一个具有AccessRights类型属性的Person,该属性决定是否可以执行某个操作。Person要么授予对此对象的访问权限,要么委托适当的方法。之后,PresidentialRights、GuardRights和PartTimeRights实现此AccessRights接口,你就可以开始了。

基于此,您永远不需要在出现新的访问权限类型时更改person类,如果出现新的操作类型,则可能需要更改person类(取决于您如何委派)并且为添加新的AccessRights类型,只需添加AccessRights的新实现即可。


0

答案是这基本上是一个设计决策。没有一种先验的正确的架构设计方式。当您定义类和它们之间的关系时,您定义了一种架构,并同时定义了表示应用程序域的语言。

与任何语言一样,它由词汇表(例如人、总统、警卫等)、语法(即您可以为词汇表实例指定的关系)和语义(即您在词汇表和关系中指定的术语的含义)组成。

现在,您显然可以以可能无限的方式获得相同的行为。对于同一系统,任何人都会提出不同的架构,因为任何人可能有不同的思考问题的方式。

尽管如此,在设计时应考虑一些标准。当您定义一个类时,您正在定义语言的“一阶”构造,当您为类定义属性时,您正在描述第一阶构造的特征。

决定你需要一个类还是一个属性的最佳方法可能是这样的。总统和警卫除了都是人之外,是否有不同的特征?如果是这种情况,并且它们具有许多不同的特征,则应创建两个类(一个用于总统,一个用于警卫),两者都继承自Person。否则,您必须将所有特征(属于人的特征,属于总统的特征和属于警卫的特征)折叠到Person类中,并将其有效性条件化为另一个标志(类型)。这将是非常糟糕的设计。
页面是否公开的特征实际上描述了页面的状态。因此,将其建模为页面类的属性是相当合理的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接