何时创建类与设置布尔标志?
我有一个有趣的问题要提出;什么时候应该创建模型类/对象而不是为数据库中存储的数据设置布尔标志?
例如,假设我有一个 Person 类,其中包含 President、Guard 和 PartTime 的布尔标志。根据标志的值,此类/模型的处理方式有所不同。因此,总统在系统中从警卫和兼职(r)那里获得了不同的特权。
什么时候会使用单表继承来表示这一信息,什么时候会继续使用布尔标志?
我的直觉是使用 STI 将它们转换为不同的对象,因为这对我来说似乎更面向对象。检查布尔值在某种程度上似乎是错误的,但我也可以看到它的一个地方。
更新澄清
让我再举一个例子,因为上面涉及的案例太多了。
我正在开发一个包含页面的 CMS 应用程序,页面可以是公共的、私有的、共享的、隐藏的或默认的(这意味着它是当您没有在 url 中指定页面时获得的内容)。现在,我们有一个页面模型,所有内容都是布尔标志 - 公共、默认、共享。
我不相信这是处理这个问题的最佳方法。特别是因为我们有规则来管理什么页面可以是什么,即默认页面或共享页面必须是公共页面,而私有页面只是私有页面。
我同意下面的评论,即“人的角色”示例很有意义。我不确定对于页面示例来说是否如此。
更复杂的是,只能有一个默认页面和一个共享页面。 STI 可能允许我验证这一点,但我不确定,因为表中可能有许多默认页面和共享页面(只是不与特定站点关联)。
注意:问题的上下文是 Ruby on Rails 应用程序,但适用于任何面向对象的语言。
I have an interesting question to pose; when should one create a model class/object as opposed to setting a boolean flag for data stored in a database?
For example, say I have a Person class that has boolean flags for President, Guard, and PartTime. This class/model is treated differently depending on the value of the flags. So the President gets different privileges in the system from the Guard and from the PartTime(r).
When would one use Single Table Inheritance to represent this information and when would one just continue to use the boolean flag?
My instinct is to convert these to different Objects using STI since this seems more OO to me. Checking booleans seems wrong in some way, but I can also see a place for it.
Update for clarification
Let me use another example because the one above has too many cases involved with it.
I am working on a CMS application that contains Pages, a Page can be Public, Private, Shared, Hidden, or Default (meaning it is what you get when you don't specify a page in the url). Right now, we have a Page model and everything is a boolean flag - Public, Default, Shared.
I am not convinced this is the best method of handling this. Especially since we have rules governing what page can be what, i.e., the Default page or a Shared page must be a Public page whereas a Private page is just Private.
I agree with the comment below that Roles for the Person example makes a lot of sense. I am not sure that for the Page example it does.
And to make things more complicated, there can only be one Default page and one Shared page. STI may allow me to validate this, but I am not sure since there can be many default and shared pages in the table (just not associated with a particular site).
Note: The context for the question is a Ruby on Rails application, but is applicable for any object-oriented language.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先,让我们确定单表继承的典型用途。它是一种将多个彼此相似的事物的存储和行为结合起来的方法。坚持使用 CMS,一个示例是包含
posts
的表格,它可以是Comment
或Article
。它们共享相似的数据和行为,但最终是不同的东西。某事物是否是注释不是对象的状态,而是一种身份。然而,在您的示例中,页面是公共的还是私有的、共享的还是非共享的还是隐藏的,似乎都是页面状态的一部分。尽管单表继承在技术上可能可行(假设所有子类都是互斥的),但它并不合适。
状态应该在一个或多个列中实现。表示某种双重状态的属性可以指定为布尔值; 是或否。如果页面始终私有或公共,您可以将其建模为单个布尔列,
私有
。如果它不是私有的,那么它就是公开的(或者反之亦然)。在某些情况下,您可能想要存储三个或更多互斥的不同状态。例如,一个页面可以是私有、公开或共享(我不知道情况是否如此——让我们假装是这样)。在这种情况下,布尔值将无济于事。您可以使用多个布尔标志,但正如您正确观察到的那样,这非常令人困惑。最简单的方法是将其建模为枚举。或者,当您缺少此功能时(如 Rails 的情况),只需使用具有特殊含义的字符串值并添加验证,以确保您使用的唯一值是
private
、public< 之一/code> 或
共享
。有时不同状态变量的某些组合无效。例如,页面可能是草稿或已批准(由布尔列
已批准
反映);并且它也是公共或私有(也由布尔列反映)。我们可以决定某个页面在公开之前必须获得批准。在这种情况下,我们宣布其中一个状态无效。这应该通过模型验证反映出来。重要的是要认识到,草稿、公开页面并非从根本上不可能,它之所以不可能,只是因为您认为它不应该发生。创建模型时,请仔细区分反映现实世界中主体的实际属性和状态的属性与确定应该可能发生的情况的业务规则以及不应该发生的事情。第一个应该建模为列,第二个应该建模为验证。
原始答案:
一个明显的区别是布尔标志允许
Person
同时被标记为总统和警卫。如果您的模型允许这些情况,那么单表继承将不适合您。另一方面,总统的
人
的行为可能与普通人不同;一个人只能担任总统或警卫。在这种情况下,继承可能更合适。不过,我认为您不应该将“兼职”建模为子类。无论如何,这都是一个属性。还有第三个重要的选项,您可以将一个人的工作或角色与模型完全分开。一个人拥有一份(或多个?)工作,这些工作可以是兼职也可以不是。该模型的优点是可以将人的属性与其工作的属性分开。毕竟,人们会换工作,但这并不会让他们真正成为一个不同的人。最终,在我看来,这似乎是模拟您的情况的最现实的方法。
First of all, let's establish what single-table inheritance typically is used for. It is a way to combine the storage and behaviour of multiple things that resemble each other. Sticking to a CMS, an example would be a table with
posts
, which could be either aComment
or anArticle
. They share similar data and behavior, but are ultimately different things. Whether or not something is a comment is not the state of the object, it's an identity.In your example, however, whether or not a page is public or private, shared or not, or hidden, appears to be a part of the state of the page. Although single-table inheritance might technically work (provided all subclasses are mutually exclusive), it's not a good fit.
State should be implemented in one or more columns. An attribute that represents a certain dual state can be specified as a boolean; yes or no. If a page always is either private or public, you can model this as a single boolean column,
private
. If it's not private it's public (or the other way around).In some cases you may want to store three or more different states that are mutually exclusive. For example, a page could be either private, or public, or shared (I don't know if this is the case -- let's pretend that it is). In this case a boolean will not help. You could use multiple boolean flags, but as you correctly observe that is very confusing. The easiest way is to model this as an enumeration. Or when you lack this (as is the case with Rails), simply use string values with a special meaning and add a validation that ensures the only values you use are one of
private
,public
orshared
.Sometimes certain combinations of different state variables are invalid. For example, a page might be a draft or approved (reflected by a boolean column
approved
); and it is also either public or private (also reflected by a boolean column). We could decide that a page should must be approved before it is made public. In this case we declare one of the states invalid. This should be reflected by the validation of your model. It is important to realise that a draft, public page is not fundamentally impossible, it's only impossible because you decide it should not happen.When creating your model, make a careful distinction between the attributes that reflect actual properties and states of the subjects in the real world, and the business rules that determine what should be possible and what shouldn't be. The first should be modelled as columns, the second as validations.
Original answer:
One obvious difference is that boolean flags allow a
Person
to be marked as president and guard at the same time. If your model should allow these situations, single-table inheritance will not work for you.On the other hand, maybe a
Person
that is a president behaves differently from a regular person; and a single person can only be president or guard. In this case inheritance may be a better fit. I don't think you should model "part time" as a subclass, though. That is an attribute in any case.There is also an important third option, one where you completely separate the job or role of a person from the model. One person has one (or many?) jobs, which are or are not part-time. The advantage of this model is that you separate attributes of a person from the attributes of their job. After all, people change jobs, but that does not make them literally a different person. Ultimately this seems to me the most realistic way to model your situation.
我不喜欢为此使用标志,但也不为此子类化 Person。相反,附加一个角色(或者如果您有一个既是总统又是警卫的人,则附加一组角色)以及管理权限的角色子类。
就我个人而言,我既不是总统也不是警卫,但我既是程序员又是音乐家,有时还担任其他一些角色(事实上,多年前我曾一度担任警卫,同时也是一名学生。) 。
一个人有一个角色。
I prefer not to use a flag for this, but also not to subclass Person for this. Rather, attach a Role (or if you have someone who's both a President and a Guard, a set of Roles) with subclasses of Role governing the prvileges.
Personally, I am neither a President nor a Guard, but I am both a Programmer and a Musician, and have a few other roles at times (in fact, I was a Guard for a while simultaneous with being a Student many years ago.).
A Person has-a Role.
我发现每当我想到“嗯,我有这 3 种类型的行为,它们看起来确实像子类,但需要在运行时更改”时,请查看策略或状态模式。它通常非常适合,并且在区分责任方面通常也胜过简单的布尔标志。
在您的情况下,这种启发式方法会说您有一个具有 AccessRights 类型属性的 Person,该属性决定是否可以执行某个操作。 Person 要么授予对此对象的访问权限,要么委托适当的方法。之后,您就拥有了实现此 AccessRights 接口的 PresidentialRights、GuardRights 和 PartTimeRights,您就可以开始了。
鉴于此,每当出现新类型的访问权限时,您都不需要更改人员类,如果出现新类型的操作,您可能需要更改人员类(取决于您是否委托以及委托方式),并且为了添加新类型的 AccessRights,您只需添加新的 AccessRights 实现。
I have found that whenever I think "Hm, I have these 3 types of behavior and they do look like subclasses, but need to change at runtime", look at a strategy or state pattern. It usually fits very well and usually also beats a simple boolean flag with respect to keeping responsiblities apart.
In your case, this heuristic would say that you have a Person with an attribute of type AccessRights, which decides if a certain action can be performed or not. Person either gives access to this object or delegates appropiate methods. After that, you have PresidentialRights, GuardRights and PartTimeRights implemetning this AccessRights interface and you are good to go.
Given this, you never need to change the person class whenever a new type of access right appears, you might need to change the person class if a new type of action appears (depends on if you delegate and how you delegate) and in order to add new types of AccessRights, you just add new implementations of AccessRights.
答案是,这基本上是一个设计决策。设计架构并不存在先验的正确方法。当您定义类和它们之间的关系时,您定义了一个体系结构,同时定义了一种代表应用程序领域的语言。
与任何语言一样,它由词汇组成(即人、总统、警卫等);语法(即您可以为词汇表实例指定的关系)和语义(即您在词汇表和关系中指定的术语的含义)。
现在您显然可以以可能无限的方式获得相同的行为。任何人都会为同一个系统提出不同的架构,因为每个人都可能对问题有不同的思考方式。
尽管如此,在设计时还是应该考虑一些标准。
当您定义类时,您正在定义语言的“一阶”构造,当您定义类的属性时,您正在描述一阶构造的特征。
决定是否需要类或属性的最佳方法可能是这样。
既然总统和卫兵都是人,那么除了他们所共有的特征之外,他们还有不同的特征吗?如果是这种情况,并且它们具有许多不同的特征,您应该创建两个类(一个用于总统,一个用于警卫),两者都继承自 Person。否则,您必须折叠 Person 类中的所有特征(属于 person 的特征、属于 President 的特征和属于 Guard 的特征),并将其有效性限制为另一个标志(类型)。这将是一个非常糟糕的设计
。页面是否公开的特征实际上描述了页面的状态。因此,将其建模为页面类的属性是非常合理的
the answer is that it is basically a design decision. There is not an a priori right way of designing an architecture. When you define classes and relationships among them you define an architecture and, at the same time, a language representing the domain of your application.
As any languages it consists of a vocabulary (i.e. Person, President, Guard, etc.); a Syntax (i.e. the relationships you can specify for the instances of your vocabulary) and Semantics (i.e. the meaning of the terms you specify in vocabulary and relationships).
Now you can obviously obtain the same behaviour in possibly infinite way. And anyone would come up with a different architecture for the same system since anyone might have a different way of thinking at the problem.
Despite this there are some criteria you should take into account when designing.
When you define a Class you are defining a "first order" construct of your language, when you define attributes for a Class you are describing the characteristics of your first order constructs.
The best way to decide if you need a class or an attribute might be this.
Do Presidents and Guards have different characteristics apart of those they share since they are both person? If that is the case, and they have a number of different characteristics you should create two classes (one for the President and one for the Guard)both inheriting from Person. Otherwise you have to collapse all the characteristics (those belonging to person, those belonging to President and those belonging to Guard) in the Person class and condition their validity to another flag (type). This would be a very bad design
The characteristic of a Page of being public or not is instead something which actually describes the status of a page. It is therefore quite reasonable to model it as a Property of the Page Class