面向对象范式问题
尽管我已经编程很长一段时间了,但当涉及到耦合对象时,我似乎总是把头撞在墙上,所以我想知道是否有人有我可以遵循的资源或黄金规则。
让我举一个小例子,没有特定的语言...
class Person {
private int personnel_id
private String first_name;
private String last_name;
private int personnel_level;
//Lab labs[4]; <- Lab(s) the Person works in
}
class Lab {
private int lab_id;
private String lab_name;
//Person[99] personnel; <- Person(s) working in the Lab
}
让我们暂时忽略 ctors/setters/getters/dtors 并实例化一些东西...
Person people = new Person[1500];
Lab labs = new Lab[10];
我的问题是..这里的最佳实践是什么...
people["Gordon Freeman"].blewUp((Lab)"Black Mesa");
-> returns T/F
或者...
labs["BlackMesa"].blownUpBy((Person)"Gordon Freeman");
-> returns T/F
或者也许这根本不重要:S
我正在研究的现实生活中的例子要复杂得多。每当Person
做某事时,Lab
中的每个人都需要得到通知,等等,我只是想弄清楚是否有任何我可以在这里应用的原则。
Even though I've been programming for quite a while now, when it comes to coupling objects I always seem to bang my head against the wall so I'm wondering if anyone has any resources or golden rules I can follow.
Let me give a small example, in no particular language...
class Person {
private int personnel_id
private String first_name;
private String last_name;
private int personnel_level;
//Lab labs[4]; <- Lab(s) the Person works in
}
class Lab {
private int lab_id;
private String lab_name;
//Person[99] personnel; <- Person(s) working in the Lab
}
Lets ignore ctors/setters/getters/dtors for now and just instantiate some stuff...
Person people = new Person[1500];
Lab labs = new Lab[10];
My question is.. what's the best practice here...
people["Gordon Freeman"].blewUp((Lab)"Black Mesa");
-> returns T/F
or...
labs["BlackMesa"].blownUpBy((Person)"Gordon Freeman");
-> returns T/F
or maybe it doesn't even matter :S
The real-life example I'm working on is a ton more complex. Whenever the Person
does something, everyone in the Lab
needs to be notified, etc, and I'm just trying to figure out if there are any principles I can apply here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我的答案是几个现有答案的组合。
这里的本质问题是,这里有一个隐藏的概念。该方法并不是真正谈论实验室对象或人员对象,而是谈论它们之间的关系。 (正如@dacris和@vs所建议的。)
处理这种情况的一种方法是使用具有双重调度的语言(谢谢@Ken。)
另一种方法是自动生成代码(谢谢@vs .) 在这种情况下,任一方向都有可用的方法。
但通常这些解决方案并不实用——为此改变整个语言似乎有点矫枉过正。
不过,自动生成的解决方案为我们提供了见解。这两种技术都应该是合法的。因此您可以手动实现这两种技术。
但是,如果您不想重复自己,这种方法可以清楚地表明任一方向都是合法的。所以不要出太多汗。
如果您正在编写一个系统,其中 Person 对象除了爆炸之外还有其他用途,那么最好将耦合从 Lab 转到 Person(即将方法放在 Lab 对象上),这样 Person 对象就可以在其他地方使用,而无需必须处理 Lab 对象的更改或与爆炸相关的方法。
...反之亦然。如果一个人所做的只是爆炸东西,那么逻辑应该是保持实验室清洁和原始(这对实验室很重要!)
My answer is a combination of several existing answers.
The essential problem here is that there is a hidden concept here. The method isn't really talking about lab object or the person object, but about the relationship between them. (As suggested by @dacris and @vs.)
One way to deal with such situations is to use a language with double-dispatch (Thank you, @Ken.)
Another way, is to have auto-generated code (Thank you @vs.) in which case there would be methods available in either direction.
But often those solutions aren't practical - changing entire languages over this seems overkill.
The auto-generated solution gives us an insight though. Both techniques should be legal. So you could implement both techniques manually.
However, if you don't want to repeat yourself, this approach makes it clear that EITHER direction is legal. So don't sweat it, too much.
If you are coding a system where the Person object has other uses apart from exploding things, it would be better for the coupling to go from Lab to Person (i.e. put the methods on the Lab object) so the Person object can be used elsewhere without having to deal with changes to the Lab object or the explosion-related methods.
... and vice-versa. If all a person does is explode things, then the logic should be there to keep the lab clean and pristine (which is important for labs!)
您可能想阅读一些有关观察者和发布/订阅模式的内容。您所描述的几乎是观察者模式的经典应用程序。发布/订阅模式基本上是相同的想法,只是稍微抽象一点以帮助扩展。
无论如何,考虑到这种模式已经广为人知,您最好遵循它的约定,除非您遇到您确实确信不这样做会受益的情况。
You might want to read a bit about the Observer and Publish/Subscribe patterns. What you're describing is pretty much the classic application for the Observer pattern. The pub/sub pattern is basically the same idea abstracted a bit more to help scaling.
In any case, given how well known this pattern already is, you might as well follow its convention unless you encounter a situation where you're really sure you benefit from doing otherwise.
想象你在说英语。一般规则是,动词(和方法)应尽可能具有“主动语态”——也就是说,一个对象应该做某事,而不是对它做某事。
如果这是一个事件,被动语态更有意义——实验室应该知道其中有哪些人,但一些随机的人(即使是在同一个实验室工作的人)可能不应该知道,所以实验室爆炸的通知最好来自实验室本身。但实际上,在这种情况下,这取决于个人(或团队)的偏好。
Think like you're speaking English. The general rule is, verbs (and methods) should have "active voice" as much as possible -- that is, an object should do something, rather than have something done to it.
If it's an event, passive voice makes a little more sense -- the Lab should know what Persons are in it, but some random Person (even one working in the same Lab) probably shouldn't, so a notification that the Lab blew up would be best coming from the Lab itself. But really, it's about personal (or team) preference in that case.
你说得对。我认为这是当今大多数面向对象系统的主要问题之一:通常,方法似乎自然地“属于”对象,但通常情况并非如此。
具有多重调度的系统巧妙地避免了这个问题。例如,在 Dylan 中,您可能会这样说:(
我链接到 c2.com MultiMethods 页面,因为我认为它在描述这一点方面做得最不差。维基百科有一个 Multiple_Dispatch 页面,但它的示例非常糟糕。 )
You're right. I think this is one of the major problems of most of today's object-oriented systems: often, methods seem to naturally "belong to" an object, but often they don't.
Systems with multiple dispatch neatly avoid this problem. For example, in Dylan, you might say something like:
(I linked to the c2.com MultiMethods page because I think it does the least-bad job of describing this. Wikipedia has a page for Multiple_Dispatch, but its example is pretty awful.)
我不完全确定你的例子意味着什么,但是一本一本优秀的书,里面有你想要的东西应用 UML 和模式 作者:Craig Larman。
这本书广泛讨论了责任分配。例如,您可以使用信息专家模式,在这种情况下,对所涉及的变量了解最多的对象将被赋予拥有该方法的责任。
I am not entirely sure what your example means, but anAn excellent book that has what you want in it is Applying UML and Patterns by Craig Larman.
The book talks extensively about assigning responsibilities. For example, you might use the Information Expert pattern, in which case, the object that has the most knowledge of the variables involved will be the one that is given the responsibility of having the method.
oO 为您提供了对此的不同视角:实际上您对人员或实验室都不感兴趣,而是对它们之间的关系感兴趣。如果您从 UML 或数据库的角度来看它,您会发现这种关系在您的(心理)模型中是一个非常新的概念。另请参阅上面的 @dacris 评论,他在其中介绍了一个新类。
如果您要使用 ORM(对象关系映射),就像使用 UML 模型进行工程时一样,这两个方法
blowsUp()
和blownUpBy()
将自动代码生成,并进行各自的运行时检查以确保其一致性。拉曼的书确实应该为您包含有关此主题的内容。
oO give you a different perspective on this: actually you're not interested in either Persons or Labs, but in a relation between them. If you look at it from a UML- or database perspective, you would see that this relation is very much a new concept in your (mental) model. See @dacris comment above as well, where he introduces a new class.
If you would use ORM (Object-Relational Mapping), like when you would do when engineering with UML models, those two methods
blowsUp()
andblownUpBy()
would be automatically code-generated, with their respective runtime checks to ensure their consistency.Larman's book should indeed contain something about this topic for you.
我认为这与现实世界和您的编码约定有关,而不是一般的良好实践。对于你的英语,我还是更喜欢打电话给people.notify(lab)。但是,如果您希望实验室拥有一些有关谁调用它、哪个人的数据,您可以执行 lab.isNotifiedBy(people)。
这里的良好实践是,当您和您的同事查看代码时,这对您和您的同事来说是有意义的,他们了解它的作用,如果他们想找到一个方法,他们知道应该从哪里开始,而不是只是继续搜索或询问您
I think that it is related to real world and your coding convention rather than general good practices. For your English, I still prefer call people.notify(lab). However, if you want your lab to have some data about who call it, which person, you can do lab.isNotifiedBy(people).
The good practice here is that it makes sense for you and your colleague when looking at the code, they understand what it does, if they want to find a method, they know where they should start rather than just keep searching or asking you
我喜欢设计这样的东西:
I like designing things like this:
在这种情况下,我想引入一个新对象 - LabExplosion
然后,在某处保留 LabExplosion 的存储库,并执行以下操作:
In this case I'd like to introduce a new object - LabExplosion
Then, keep a repository of LabExplosions somewhere, and do something like:
这取决于您的用例,用户将如何使用系统?实验室会被
人
“炸毁”吗?或者系统的用例是让一些Person
炸毁Labs
?最后的结果是相同的,但这里重要的是代码的语义。如果让实验室被人炸毁听起来很愚蠢,那就不要这样做。
因此,正如 BobTurbo 提到的,黄金法则是找出“信息专家”(请参阅:GRASP )在系统中并将控制权交给该对象。
您通常会定义有关如何使用系统的用户历史记录或叙述,例如,如果叙述是:
然后,对我来说,这意味着一个
人
在实验室
工作,当创建后,该人可以接收他所工作的实验室,并注册自己以获得有关该 perticula 实验室中发生的情况的通知。由于实验室有要通知的人员列表,因此执行通知的实验室是有意义的(在本例中,人员将控制权交给实验室)
Person
可以定义为:那么可能 ,该人在实验室中做了一些事情,在本例中是公共方法
blowUpLab
,它通过调用实验室的boom!
方法来炸毁该人的实验室。反过来,
Lab
执行方法操作并在最后通知所有订阅者:这就是观察者模式。
最后,您的主应用程序将创建人员和实验室并执行用例:
此主应用程序将创建三个人,与一些实验室,其中只有两个人相关( scott 和 gordon ),
其中一个随机将收到
blowUpLab
消息并将执行该方法。实验室将依次通知该实验室的所有订阅者。因此,当 James Hetfield 破坏其实验室时,没有人会收到通知:)
重点是一定要描述您的用例,并确定那里的信息专家;将控制权交给该对象,并让该对象将控制权依赖于其他对象,但仅根据您的用例
我希望这是有意义的。
It depends on your use case, how is the user going to use the system?. Would it be a Lab being "blowed" by a
Person
? or the use case of the system is to have somePerson
blow upLabs
?At the end the result is the same, but the important thing here is the semantic of the code. If sounds silly to have Labs being blowed by people, then don't do it.
So the golden rule, as BobTurbo mention, is to find out the "information expert" ( see: GRASP ) in the system and give the control to that object.
You usually define a user history or narrative on how the system would be used, if, for instance, the narrative is:
Then, to me it means that a
Person
works in aLab
, when it is created, that person may receive the lab he works on, and register himself to be notified of what happens in that perticula lab.Since the lab has the list of the persons to notify, it makes sense to be the lab who performs the notification ( Person gives control to Lab in this case )
Then probably the
Person
could be defined as:So, the person does something in the lab, in this case the public method
blowUpLab
which just blows up the person's lab by invoking the Lab'sboom!
method.In turn the
Lab
perform the method actions and notify all its subscribers at the end:This is the observer pattern.
Finally your main app will create persons and labs and execute the use case:
This main app, will create three person, with some lab, only two of them are related ( scott and gordon )
Randomly one of them will receive the
blowUpLab
message and will perform the method. The lab in turn will notify all the subscribers of that lab.So, when James Hetfield, blow its lab, no one will be notified :)
The point is Do describe your use case, and identify the information expert there; give the control to that object, and let that object rely the control to other object, but only according to your use case
I hope it makes sense.