首页 新闻 搜索 专区 学院

一个关于类设计的,简单却常见的问题。

0
悬赏园豆:100 [待解决问题]

通常我们在设计类的时候,会涉及到类的属性和行为,但是很多时候,我们却很难将不同类的属性和行为划分明显的界限。这叫好像进行-(-1)=1一样,所以我的问题就是:比如,一个图书管理系统,实体有借阅者,书这两个实体,对于借阅者来说是可以设置借阅时间这个属性,这个时间代表的意思是说,这个人可以借阅任何一本书的最大时间。但是我们如果将这个属性放到书这个实体上,这个属性进行转换一下,暂叫做可被阅读时间。事实上这两个属性都是表示可以阅读的时间,因为放置的对象不同,其被表现的方式就不同。我想请教下大家,这个属性到底是放在借阅者这个类好,还是放在书这个实体好,同时给出你分析的思路和原因。因为这类问题在设计类的时候会经常遇见到,谢谢大家。

super mans的主页 super mans | 初学一级 | 园豆:14
提问于:2011-12-12 16:20
< >
分享
所有回答(8)
0

书有各种属性。。作者什么的,如果这个借阅的时间是一定的,比如最多能借3天。那么我认为应该把这个时间当作书的一个属性。。。

Rookier | 园豆:652 (小虾三级) | 2011-12-12 16:25

是的,书可以有很多属性,但是“借阅时间”这个属性,并不能说明这个实体是一个类,所以可以说它不是书的一个属性。

支持(0) 反对(0) super mans | 园豆:14 (初学一级) | 2011-12-13 09:03

书是可以有很多属性,但是“可借阅的时间”这个属性并不一定能说明这个实体就是书,如果这本书没放在图书室那么它就不具备“可借阅的时间”这一说法,但是比如像“出版社”这个属性,不管书是在商店还是在图书馆,它依旧会存在,所以我认为“可借阅的时间”这个属性可以不放在书这个实体中。

支持(0) 反对(0) super mans | 园豆:14 (初学一级) | 2011-12-13 09:08
0

其实你说的因该是Transaction的数据,他可以出现在书中,也可以出现在借阅者中。

其实实体类的属性方法的划分,以方便为主。像你上面的例子,通常来说放至书的属性中较好,无它,只是方便而已。

 

还有面向对象并不一定要将现实世界中的实体映射过来,这样太死板,也不是面向对象的初衷。

Jerry Chou | 园豆:2642 (老鸟四级) | 2011-12-12 16:32

我比较喜欢你最后一句话。但是如果将这个属性放在书中,那书这个实体的属性"借阅书籍的日期"该怎么处理呢。

支持(0) 反对(0) super mans | 园豆:14 (初学一级) | 2011-12-13 09:02
0

设计是跟着需求走的。如果设计中遇到类似ambiguous的问题,建议先搞清楚需求。

我在这里若列出两个不同的需求,对应这不同的设计。

1, 最长借阅时间和最长被节约时间是一回事。建议把这个属性放在书的类型中,因为一本书可能被多个借阅证借阅,这里主要是出于节省空间的考虑。甚至说,如果所有的书最长时间都是一样的,可以把这个时间属性作为static放在书的抽象类中。

2, 最长借阅时间和最长被节约时间不是一回事。不如说不同级别的借阅者最长借阅会不同,不同的书有着不同的最长被借阅时间,那当然借阅者和书都需要一份最长时间属性了。

胡屯 | 园豆:714 (小虾三级) | 2011-12-12 16:39

先赞一个,你的回复速度很快,你分析问题的方法很不错。我再思考下,先吃个饭。

支持(0) 反对(0) super mans | 园豆:14 (初学一级) | 2011-12-12 16:43
1

我两个地方都不会放。很明显“能借多久”或者“被阅读多久”,这种都不是“固有属性”。某图书馆规定,所有借出去的书最多只借180天,那么这其实是图书馆的规定(业务逻辑),跟书本身没有关系,这本书如果是放在另外一个图书馆,那么很可能就是其他的逻辑。你说的这个借书系统,大概应该是这样设计:

public class Book //包含ID,Name等一些固有属性
public class Reader //包含ID,Name...
public class BorrowRecord
{
public long BookID { get; set; }
public long ReaderID { get; set; }
public DateTime BorrowTime { get; set; }
public decimal Deposit { get; set; }
}

以上3者并不包含任何业务逻辑,他们都只是模型。接下来这个方法将很容易给图书管理员列出那些逾期未还的记录:

public List<BorrowRecord> ListOverdue()
{
//查询所有BorrowTime为DateTime.Now之前180天的
//当业务逻辑发生修改时,你要修改的都是这里
//数据保证了充分的完整性和与业务逻辑的不相关性
}

如果你有需求是:小说最多被借180天,杂志最多被借30天等等。那么你应该有个这样的表(可以维护):

  书籍类型        说明         最长借阅天数
1 小说 180
2 杂志 30

Book类有个属性叫“书籍类型”(它一般是个enum)。然后你通过一次join就能够查出哪些是超出时间的。总之原则就是业务逻辑不要与模型耦合太厉害。(其实这句话严格来讲也有问题,有的架构设计就是相反的。这里不多说了……)

水牛刀刀 | 园豆:6350 (大侠五级) | 2011-12-12 16:39

你分析问题很有见地,英文也很好,赞一个。我先说下我的看法,关于类的设计通常会涉及到属性,行为和合作关系。上面我遇到的问题,肯定很多人也会遇到,从某种意义上来说,借阅和被借阅其实表达的意思是一样的,只是看问题的角度不一样而已。我的分析是:不管是借阅者还是书,借阅时间或被借阅时间这两个属性都不能证明这个实体就是一个借阅者或者是书,所以这两个不应该成为借阅者或者书的属性(或职责)。因此我想把这个关于时间的抽象出来一个新类叫做Dates类,在这个类中我保证它的职责是1.知道当前系统的时间,2.能比较两个时间并计算出时间差。上面你提到计算某个时间段下的一些书籍这个业务逻辑,我们只需要用Dates类中的比较时间差的功能,用系统当前日期减去书这个实体的借阅日期就能得到某个时间段下的书籍集合,同时这个‘180’天可以实现动态的传入,算是比较灵活。当然很多时候也会出现无法抽象成新类的解决办法,所以我很纠结。谢谢大家给出的分析思路让我深受启发,真希望能和大家做个朋友。

支持(0) 反对(0) super mans | 园豆:14 (初学一级) | 2011-12-13 08:59
0

对借阅者和书这两个实体来说,借阅的时间这个两个属性应该不是同一个概念,从借阅者出发,时间的概念应该是“他借一本书的最长时间”。而对于书这个实体来说,时间这个属性应该理解为“一本书的最长借阅时间”,这两者可能是相等的,也可能是不相等的。借阅者可以有教师,学生。在借阅的时候,教师的借阅时间一般会大于书正常最大借阅时间,而学生的最久借阅时间应该是小于书的正常最大借阅时间的。这样去分析,我想应该这个属性怎么去设计就应该清楚了。

在实际的类设计过程中,我们根据实际的情景去分析问题,应该很少会出现你说的情况的吧。

豆腐欣欣 | 园豆:76 (初学一级) | 2011-12-12 21:18

恩,你说的很有道理。

支持(0) 反对(0) super mans | 园豆:14 (初学一级) | 2011-12-13 09:00
0

两个地方都不适宜放。

因为借阅时间在实际中是一种经常变化的业务逻辑,并非读者或书本的固有属性。

比如读者,图书馆将来很可能实行这样的业务,普通读者能借7天,付费的vip读者能借1个月,这时借阅时间就和用户角色绑定了,又如书本本身,图书馆可以根据被借阅的频率(即热门程度)划分借阅时间,或者根据书本本身的一些属性,比如页数,内容来划分最长借阅时间,这样就能更好的为读者服务,最后借阅时间成了借阅策略的计算结果。

lindping | 园豆:4 (初学一级) | 2011-12-14 09:58
0

我的观点和楼上相反呵呵:两个地方都放。适当冗余可以简化复杂的业务逻辑,增加扩展性,简化操作。

【当耐特】 | 园豆:645 (小虾三级) | 2011-12-15 08:39
0

我的观点,比楼上和楼上的楼上都夸张。

借阅时间应该放在三个地方:

1。书或者书的关联实体上,这不是书本身的基础属性,而是其中的扩展属性。这个扩展属性是可以改变的。

2. 借阅者身上。是借阅者的借书约束之一。

3. 借阅流程水帐上。 这个时间从以上2个属性产生关联(也就是借阅这个动作)的时候计算出。这本书这个借阅者能借几天。

 

其实一开始就这样去设计,往往把事情复杂化。那么我们可以先从最简单的开始,遵循单一职责,为以后的扩展留下余地。

具体取舍要看你的实际业务要求:

假设 当前需求下,书的借阅时间,是由书或者书的扩展属性(比如来类别)来决定借阅时间的,则应该将 借阅时间 放在书的关联类上。在借阅时,计入 借阅流水。

另一个方向,如果借阅时间是按照 人或者人的类别来分的(比如 老师能借1个月,但学生只能借15天),则应该将借阅时间放在人或者人多关联上。在借阅时,计入 借阅流水。

 

个人浅见。

红色壁虎 | 园豆:202 (菜鸟二级) | 2012-01-14 17:45
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册