面向对象设计,定义关系
在设计相对较大的系统时,我对 OOD 感到非常困惑。我们总是谈论两个实体之间的关系。我的问题是哪一个拥有另一个?
设计停车场时,有很多停车位。汽车应该有一个称为停车位的数据字段,还是停车位应该容纳汽车?或者两者兼而有之?
在图书馆预订系统中,我假设有一个图书馆类、图书类和用户类。用户应该调用 checkout(book),还是图书调用 checkout(user),或者图书馆调用 checkout(book, user)?
这让我很困惑。欢迎任何评论/建议。
百合
I am getting really confused about OOD when designing relatively large system. Always, we talk about has-a relationship between two entities. My question is about which one owns the other?
when designing a parking lot, there are many parking space. Should the car has an data field called parking space, or should the parking space hold the car? or both?
in a library reservation system, I am assuming there is a class Library, a class Book, and a class User. Shall the user call checkout(book), or the book call checkout(user), or the library call checkout(book, user)?
It's been very confusing for me. Any comment/suggestion is welcomed.
Lily
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这取决于具体情况以及您所说的“自己”的含义。
在您的第一个示例中,汽车和停车位之间存在一对一的关系。从数据库的角度来看,您必须判断哪个表“拥有”另一个(哪个表“拥有”外键)。例如,您可以根据预期的使用情况做出此判断,因为停车位可能保持固定,但汽车一直在进进出出,因此停车场“拥有”汽车可能更符合逻辑。这就是你的设计技巧发挥作用的地方。
在第二个示例中,在我看来,一次只能向一个用户借出一本书,而“借出”是发生在一本书上的操作。因此,正确的解决方案是
Book.checkout(user)
。在此基础上,用户可能一次结帐多本书,因此我倾向于在 Library 上有一个结帐方法,例如Library.checkout(Books[], user)
依次调用Book.checkout(user)
。It depends on the situation, and what you mean by "own".
In your first example there is a one-one relationship between a car and a parking space. From a database perspective you will have to make a judgement about which should "own" the other (which table 'owns' the foreign key). You would base this judgement on expected usage - for example - since a parking space is likely to remain fixed, but you have cars coming and going all the time, it might make more logical sense for the carpark to "own" the car. That's where your design skills come into play.
In the second example, it seems to me that a single book can only be checked out to one user at a time, and "checking out" is an action that occurs on a book. Therefore the correct solution is
Book.checkout(user)
. Building on that, a user is likely to checkout more than one book at a time, so I would be inclined to do have a checkout method on Library, such thatLibrary.checkout(Books[], user)
calledBook.checkout(user)
in turn.对于#1,停车位应记录其是否被占用,如果被占用,则记录里面有什么车。否则,要查看是否可以在某个地方停车,您必须轮询每辆车以查看它们是否在该位置。
我对#2 的直接想法是它应该是
Library.checkout(Book, User)
,这样您就可以记录用户已借出一本特定的书籍。然而,这在很大程度上取决于您想要做什么,并且您应该以最容易解决当前问题的方式进行设计。
有时,只要保持同步,在两个地方复制数据并不是一个糟糕的主意。例如,如果您需要知道特定汽车的停放位置,您最终将希望在
Car
类中跟踪该数据,否则您将得到轮询每个停车位以了解该车是否停在那里。For #1, the parking space should keep a record of if it is occupied and if so, what car is in it. Otherwise to see if you could park somewhere, you would have to poll every car to see if they are in that spot.
My immediate thinking for #2 is that it should be
Library.checkout(Book, User)
such that you make a note that a User has checked out a specific book.This is heavily dependent on what you're trying to do however, and you should design it in such a way that is easiest for the problem at hand.
Sometimes replicating the data in two places isn't a terrible idea as long as you keep it synchronized. For instance, if you need to know where a specific car is parked, you're going to end up wanting to keep track of that data in the
Car
class as well, otherwise you're going to have to poll every parking spot to know if that car is parked there.在面向对象设计中,对象可以被视为一个实体。此时,您可以使用实体关系建模来更好地了解谁必须拥有什么。
当您设计模型时,您不应该关心如何实现它。我的意思是,您不应该考虑谁将拥有什么,因为这是设计的另一个过程,即将模型转换为对象(可以是数据表、XML、C# 对象……):仅此时,根据实体获得的关系,您可以决定谁必须拥有什么(有时甚至违反要求,我稍后将向您展示)。
在设计时,您必须只关注您的需求。在汽车和停车位的情况下,您应该考虑:
一辆汽车可以容纳多少辆停车位?
一个公园可以停放多少辆车?
我的系统要回答什么样的答案?例如:作为用户,我想根据车牌号知道汽车停在哪里(在这种情况下,之前的答案将是错误的,因为如果您让公园拥有这辆车,您应该遍历公园以获取上面有什么车)
正如您所看到的,这实际上取决于您的业务需求,尤其是当您具有“一对一”关系时(如本例所示)。
所以我建议你看看“实体关系建模”并坚持它的概念来更好地设计你的对象模型。
In Object Oriented design the object can be considered an entity. At this point you may use the Entity relationship modelling to better understand who has to own what.
When you design your model you shouldn’t care how you are going to implement it. I mean you shouldn’t think who is going to own what because this is another process of the design that is when you are going to convert your model to objects (that can be data table, XML, C# object ,…. ) : only at this point against the relationship the entity got you can decide who has to own what(sometime even against the requirements as I’ll show you later).
At the design time you must focus just on the requirements you have. In the case of the car and car parking you should think about :
How many park car one can occupied ?
How many cars a park can host ?
What kind of answer has my system to answer ? EX: as user I want know where a car is parked against its car plate number (in this case the previous answer would be wrong because if you let the park own the car you should iterate through the park to get what car is on it)
As you can see it really depends by you business requirements especially when you have “one-to-one” relationship(as in this case).
So I can suggest you to have a look at “Entity relationship Modelling” and stick to its concept to better design you object model.
在这种情况下,毫无疑问停车位应该容纳一辆车(这称为聚合),因为汽车和停车位之间的关系不是永久的(不同的汽车可以在同一天停在同一个停车位)
在此我认为,在这种情况下,用户想要获取一本书,因此该系统的 GUI 必须有一些按钮(或其他按钮),用户必须单击该按钮才能预订一本书。因此,用户调用系统(对象库代表它)的方法
checkout(book)
来检查这本书是否免费(或可用)。然后系统(Library)检查该书是否未被其他用户提前预订,因此它为该书的所有实例调用方法Book.check()
。在这样的解决方案中,系统中的每个用户帐户都应该有一个方法Book.check()
使用的保留书籍列表。In this case undoubtedly parking space should hold a car(it's called aggregation), because the relationship between car and parking space isn't permanent(different cars can be parked in the same parking place in the same day)
In this case, I think, the user wants to get a book, so the GUI of this system must have some button(or smht else) that user has to click to reserve a book. So, user calls a method
checkout(book)
of the system(object Library represents it) to check if the book is free(or available). Then the system(Library) checks whether the book wasn't reserved earlier by other user, so it calls methodBook.check()
for all instances of this book. In such solution every user account in the system should have a list of reserved books which methodBook.check()
uses.跳出框框思考,我不认为您提供的示例具有自然的“拥有”或“拥有”关系,并且存在比“拥有”或“拥有”更多的关系。在我看来,我想在你的例子中使用松散耦合的关系,从实现的角度来看,我会使用地图来描述和维护这种关系,这意味着,对于停车场和汽车,我会放一张地图在 Parking 类中,并将其插槽映射到汽车,如果我们发现地图中存在插槽,我们就知道该插槽已被占用,如果没有,则它是一个空闲插槽,对我来说,说 car 没有多大意义拥有插槽或插槽拥有汽车;对于图书馆的示例,同样的事情,书籍与其借阅者的关系非常松散,我会在 Library 类中放置一个映射,并将书籍映射到其借阅者。真正执行结帐操作的人是谁?它要么是图书馆工作人员,要么是自动机器,要么只是图书馆,所以我们可以有一个library.checkout(user, books),并在方法内部,将书籍和用户放入地图中。
顺便说一句,什么是真正的“拥有”关系场景? not a man has a car,这并不是真正的“has a”,即使我们在句子中有“has a”(不要让人类语言误导你),这只是意味着,在汽车的类别中,有一个名为ownerName 或ownerId 的字符串字段,仅此而已。真正的“有一个”关系场景的一个例子是人类有一颗心脏或汽车有一个引擎,这意味着在实现中,Human 类中确实存在一个 Heart 类字段。
面向对象的设计是多么美妙啊!
To think out of box, I don't think the examples you provided have a natural "has a" or "owns a" relationship, and there are more relationships than "has a" or "owns a". In my opinion, I'd like to use a loosely coupled relationship for your examples, in implementation perspective, I would use a map to describe and maintain this relationship, which means, for a parking lot and a car, I would put a map in the Parking class, and map its slots to cars, if we find a slot existing in the map, we know that slot is occupied, if not, it's a free slot, for me, it doesn't make much sense either saying car owns the slot or the slot owns the car; for the library example, the same thing, the book and its borrower are in a very loose relationship, I'd put a map in the Library class, and map the book to its borrower. And who's the guy really does the checkout action? it's either the library staff or the auto machine or simply the library, so we can have a library.checkout(user, books), and inside the method, put books and user into the map.
For the bonus, what is really a "has a" relationship scenario? not a man has a car, this is not really a "has a", even we have "has a" in the sentence (don't let the human language mislead you), this just means, inside the car's class, there is a String field called ownerName or ownerId, that's it. One example for a real "has a" relationship scenario is human has a heart or a car has an engine, which means, in the implementation, there is really a Heart class field inside the Human Class.
How beautiful the object oriented design is!