【问题标题】:When to create a class vs setting a boolean flag?何时创建类与设置布尔标志?
【发布时间】:2011-05-26 04:57:47
【问题描述】:

我有一个有趣的问题要提出;什么时候应该创建一个模型类/对象,而不是为存储在数据库中的数据设置一个布尔标志?

例如,假设我有一个 Person 类,其中包含总统、警卫和兼职的布尔标志。根据标志的值,此类/模型的处理方式不同。因此,总统在系统中从 Guard 和 PartTime(r) 获得不同的权限。

什么时候会使用单表继承来表示这些信息,什么时候会继续使用布尔标志?

我的直觉是使用 STI 将这些转换为不同的对象,因为这对我来说似乎更面向对象。检查布尔值在某种程度上似乎是错误的,但我也可以看到它的位置。

更新说明

让我再举一个例子,因为上面的例子涉及的案例太多了。

我正在开发一个包含页面的 CMS 应用程序,一个页面可以是公共的、私有的、共享的、隐藏的或默认的(这意味着当你没有在 url 中指定页面时你会得到它)。现在,我们有一个 Page 模型,一切都是布尔标志 - Public、Default、Shared。

我不相信这是处理此问题的最佳方法。特别是因为我们有管理什么页面可以是什么的规则,即默认页面或共享页面必须是公共页面,而私有页面只是私有页面。

我同意下面的评论,即 Roles for the Person 示例很有意义。我不确定它是否适用于 Page 示例。

为了让事情变得更复杂,只能有一个默认页面和一个共享页面。 STI 可能允许我验证这一点,但我不确定,因为表中可能有许多默认页面和共享页面(只是与特定站点无关)。

注意:问题的上下文是 Ruby on Rails 应用程序,但适用于任何面向对象的语言。

【问题讨论】:

    标签: ruby-on-rails oop object


    【解决方案1】:

    首先,让我们确定单表继承的典型用途。它是一种将多个彼此相似的事物的存储和行为结合起来的方法。以 CMS 为例,一个带有posts 的表可以是Comment 或Article。它们共享相似的数据和行为,但最终是不同的东西。某物是否是评论不是对象的状态,而是一种身份。

    但是,在您的示例中,无论页面是公开的还是私有的、共享的还是隐藏的,似乎都是页面状态的一部分。虽然单表继承在技术上可能可行(假设所有子类都是互斥的),但它并不适合。

    状态应该在一个或多个列中实现。表示某种对偶状态的属性可以指定为布尔值; 是或否。如果页面始终是私有或公共,您可以将其建模为单个布尔列 private。如果它不是私有的,它就是公共的(或相反)。

    在某些情况下,您可能希望存储三个或更多互斥的不同状态。例如,一个页面可以是私有的、公共的或共享的(我不知道是不是这种情况——让我们假设它是)。在这种情况下,布尔值将无济于事。您可以使用多个布尔标志,但正如您正确观察到的那样,这非常令人困惑。最简单的方法是将其建模为enumeration。或者当您缺少此功能时(如 Rails 的情况),只需使用具有特殊含义的字符串值并添加验证,以确保您使用的唯一值是 private、public 或 shared 之一。

    有时不同状态变量的某些组合无效。例如,页面可能是草稿或已批准(由布尔列approved反映);它也是 public 或 private(也由布尔列反映)。我们可以决定一个页面应该必须在公开之前获得批准。在这种情况下,我们声明其中一个状态无效。这应该通过模型的验证反映出来。重要的是要意识到草稿,公开页面根本上并非不可能,只是因为你认为它不应该发生,所以不可能。 p>

    在创建模型时,请仔细区分反映现实世界中主体实际属性和状态的属性,以及决定什么的业务规则应该是可能的,什么不应该是。第一个应建模为列,第二个应建模为验证。


    原答案:

    一个明显的区别是布尔标志允许Person同时被标记为总统和守卫。如果您的模型应该允许这些情况,那么单表继承将不适合您。

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

    还有一个重要的第三个选项,您可以将一个人的工作或角色与模型完全分开。一个人有一份(或多份?)工作,可以是兼职,也可以不是兼职。该模型的优点是您可以将一个人的属性与他们的工作属性分开。毕竟,人们会换工作,但这并不能使他们真正成为一个不同的人。最终,在我看来,这似乎是模拟您的情况的最现实方法。

    【讨论】:

    • 是的,我同意,不幸的是,我的例子对于我所面临的具体情况来说是一个糟糕的例子。我已经更新了我的帖子以包含我正在查看的具体问题。
    【解决方案2】:

    我不喜欢为此使用标志,也不喜欢为此使用 Person 子类。相反,附加一个角色(或者如果你有一个既是总统又是警卫的人,一组角色)和角色的子类来管理权限。

    就我个人而言,我既不是总统也不是警卫,但我既是程序员又是音乐家,有时还会担任其他一些角色(事实上,我曾当过一段时间的警卫和多年的学生前。)。

    一个人有一个角色。

    【讨论】:

    • 使用了一个不好的例子,请看我上面的更新。虽然完全同意你的看法,但我不确定它是否适用于实际情况。不过我需要考虑更多。
    【解决方案3】:

    我发现每当我想“嗯,我有这 3 种行为,它们看起来确实像子类,但需要在运行时更改”时,请查看策略或状态模式。它通常非常适合,并且通常在保持责任分离方面也优于简单的布尔标志。

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

    鉴于此,无论何时出现新的访问权限类型,您都无需更改人员类,如果出现新的操作类型,您可能需要更改人员类(取决于您是否委托以及委托方式)和为了添加新类型的 AccessRights,您只需添加 AccessRights 的新实现。

    【讨论】:

      【解决方案4】:

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

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

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

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

      决定是否需要类或属性的最佳方法可能是这样。 总统和警卫除了他们共同的特征之外,是否有不同的特征,因为他们都是人?如果是这种情况,并且它们具有许多不同的特征,您应该创建两个类(一个用于总统,一个用于警卫)都继承自 Person。否则,您必须折叠 Person 类中的所有特征(属于 person、属于 President 和属于 Guard 的特征),并将它们的有效性设置为另一个标志(类型)。这将是一个非常糟糕的设计

      页面的公开与否的特征是实际描述页面状态的东西。因此将其建模为页面类的属性是很合理的

      【讨论】:

        猜你喜欢
        • 2015-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-08
        • 1970-01-01
        • 2013-12-21
        • 1970-01-01
        相关资源
        最近更新 更多