当一个对象需要修改另一个对象时的 OOP 最佳实践
(这是一个类似 C 的环境)假设我有两个实例对象,一辆汽车和一个 bodyShop。该车有颜色 iVar 和相应的配件。 bodyShop 有一个名为“paintCar”的方法,它将接收汽车对象并更改其颜色。
就实现而言,为了让 bodyShop 实际上能够更改汽车对象的颜色,我看到了两种方法。
使用“&”运算符传递一个指向汽车的指针。然后 bodyShop 可以告诉汽车执行某种必须改变颜色的方法,或者可以直接使用汽车的访问器。
按值传递汽车对象,执行相同的操作来更改颜色,然后让该方法返回具有新颜色的汽车对象。然后将原始汽车对象分配给新汽车对象。
选项 1 对我来说似乎更简单,但我想知道它是否符合 OOP 最佳实践。一般来说,“最大 OOP”是“&”运营商好还是坏?或者,也许我完全错过了一个更好的选择,可以让这个超级 OOPer。请指教 :)
(this is a C-like environment) Say I have two instance objects, a car and a bodyShop. The car has a color iVar and corresponding accesors. The bodyShop has a method named "paintCar" that will take in a car object and change its color.
As far as implementation, in order to get the bodyShop to actually be able to change a car object's color, I see two ways to go about it.
Use the "&" operator to pass in a pointer to the car. Then the bodyShop can either tell the car to perform some method that it has to change color, or it can use the car's accessors directly.
Pass in the car object by value, do the same sort of thing to get the color changed, then have the method return a car object with a new color. Then assign the original car object to the new car object.
Option 1 seems more straightforward to me, but I'm wondering if it is in-line with OOP best practices. In general for "maximum OOP", is the "&" operator good or bad? Or, maybe I'm completely missing a better option that would make this super OOPer. Please advise :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
这取决于车身修理厂的方法是否会失败并使汽车处于不确定状态。在这种情况下,您最好对汽车的副本或汽车所有相关属性的副本进行操作。然后,只有当操作成功时,您才能将这些值复制到汽车中。因此,您最终会在车身修理厂方法中将新车分配给旧车。正确执行此操作对于 C++ 中的异常安全是必要的,并且可能会导致令人讨厌的。
也可以并且有时需要使用其他模式 - 在修改时返回一个新对象。这对于需要撤消/重做、回溯搜索的交互式系统以及涉及对象系统如何随时间演变进行建模的任何事物都非常有用。
It depends on whether the body shop's method can fail and leave the car in an indeterminate state. In that case, you're better off operating on a copy of the car, or a copy of all relevant attributes of the car. Then, only when the operation succeeds, you copy those values to the car. So you end up assigning the new car to the old car within the body shop method. Doing this correctly is necessary for exception safety in C++, and can get nasty.
It's also possible and sometimes desirable to use the other pattern - returning a new object on modification. This is useful for interactive systems which require Undo/Redo, backtracking search, and for anything involving modelling how a system of objects evolves over time.
除了其他选项之外,选项 1 让 PaintCar 方法返回一个完成代码,指示汽车是否已成功更改颜色或存在问题
In addition to other optinions, option 1 lets paintCar method return a completion code that indicates if the car has changed the color successfully or there were problems with it
首选方案 1:
更好的是...创建一个 IPaintable 界面。让 Car 实现 IPaintable。让 BodyShop 依赖于 IPaintable 而不是 Car。这样做的好处是:
Option 1 is prefered:
Even better still...create an IPaintable interface. Have Car implement IPaintable. Have BodyShop depend on IPaintable instead of Car. The benefits of this are:
我认为 bodyShop 的职责是修改汽车对象,所以#1 对我来说似乎是正确的方法。我从来没有使用过带有“&”的语言运营商是必要的。通常,我的 bodyShop 对象会调用 car.setColor(newColor) ,就是这样。这样您就不必担心原车的其余属性,包括持久性问题 - 您只需将它们放在一边即可。
I would assume that the responsibility of the bodyShop is to modify car objects, so #1 seems like the right way to go to me. I've never used a language where the "&" operator is necessary. Normally, my bodyShop object would call car.setColor(newColor) and that would be that. This way you don't have to worry about the rest of the original car's attributes, including persistence issues - you just leave them alone.
由于您对最佳 OOP 实践感兴趣,因此您应该忽略选项 2 对性能造成的影响。您唯一应该感兴趣的是任一选项都不必要地增加了两个类之间的耦合、是否违反了封装以及是否保留了身份。
鉴于此,选项 2 不太理想,因为您无法确定哪些其他对象持有对原始汽车的引用,或更糟糕的是,无法确定哪些其他对象包含汽车。简而言之,您违反了身份约束,因为系统中的两个对象可能对汽车的状态有不同的想法。您面临着使整个系统不一致的风险。
当然,您的特定环境可能会避免这种情况,但避免这种情况肯定是最佳实践。
最后一点,你的 bodyShop 对象有状态吗?行为和身份?我意识到您只解释了最低限度的必要内容,但 bodyShop 可能并不是真正的对象。
函数式 v OO 方法
有趣的是,选项 2 接近函数式编程环境中的方法 - 由于不允许更改状态,因此唯一的方法是创建一辆颜色发生变化的新车。这不完全是你的建议,但已经很接近了。
这听起来可能完全是矫枉过正,但它确实对证明代码和并行性的正确性有一些有趣的影响。
Since you're interested in the best OOP practice, you should ignore the performance hit you get with option 2. The only things you should be interested in is do either option unnecessarily increase coupling between the two classes, is encapsulation violated and is identity preserved.
Given this, option 2 is less desirable since you can't determine which other objects are holding references to the original car or worse, contain the car. In short you violate the identity constraint since two objects in the system may have different ideas of the state of the car. You run the risk of making the overall system inconsistent.
Of-course your particular environment may avoid this but it certainly would be best practice to avoid it.
Last point, does your bodyShop object have state; behaviour and identity? I realise that you have explained only the minimum necessary but possibly the bodyShop isn't really an object.
Functional v OO approaches
As an interesting aside, option 2 would close to the approach in a functional programming environment - since state changes are not allowed, your only approach would be to create a new car if it's colour changed. That's not quite what you're suggesting but it's close.
That may sound like complete overkill but it does have some interesting implications for proving the correctness of the code and parallelism.
选项 1 对我来说胜出。的&运算符在许多 OO 语言(如 Java、Python 等)中是隐含的。在这些语言中,您不经常使用“按值传递”——只有原始类型以这种方式传递。
选项 2 存在多个问题:您可能拥有一组汽车,某些不知道的功能可能会将汽车发送到 bodyShop 进行喷漆,收到新车作为回报,并且不更新您的汽车收藏汽车。看?从更意识形态的角度来看 - 每次你想在现实世界中修改它时,你不会创建新的对象 - 为什么你应该在虚拟世界中这样做?这会导致混乱,因为它是违反直觉的。 :-)
Option 1 wins for me. The & operator is implicit in many OO languages (like Java, Python etc). You don't use "passing by value" in that languages often - only primitive types are passed in that way.
Option 2 comes with multiple problems: You might have a collection of cars, and some function unaware of it might send a car to bodyShop for painting, receive new car in return and don't update your collection of cars. See? And from more ideologic point of view - you don't create new object each time you want to modify it in real world - why should you do so in virtual one? This will lead to confusion, because it's just counterintuitive. :-)
我不确定这个“类C环境”是什么意思。在C中,你需要这样:
在哪里修改car指向的内容。对于 C 中的大结构体,您应该始终将指针而不是值传递给函数。因此,请使用解决方案 1(如果“&”指的是 C 运算符)。
I am not sure what this "C-like environment" mean. In C, you need this:
where you modify the contents pointed by car. For big struct in C, you should always pass the pointer, rather than the value to a function. So, use solution 1 (if by "&" you mean the C operator).
我也同意第一个。我不能说这是最佳实践,因为我永远不确定其他人心目中的最佳实践是什么......我可以告诉你,我心目中的最佳实践是最简单的方法为工作而工作。我还在 hunspell win api 和我必须使用的其他 c-ish api 中看到了这种方法。所以是的,我同意斯科特的观点。
http://hunspell.sourceforge.net/
//以防万一您有兴趣查看其他人代码
I too agree with the first 1. I can't say it's best practice because i'm never really sure what best practice is in other peoples minds... I can tell you that best practice in my mind is the most simple method that works for the job. I've also seen this aproach taken in the hunspell win api and other c-ish api's that i've had to use. So yea i agree with scott.
http://hunspell.sourceforge.net/
//just in-case your interested in looking at other peoples code