OO设计:引入新字段的子类的通用处理
这是我一直遇到的一个设计问题,所以我想我最终会把它放在那里,看看人们会如何处理它。问题如下:
我确定了一个特定的类,它在很大程度上描述了我将使用的对象的所有实例,包括行为和数据方面。这非常棒,并且对于基本对象来说效果很好。然后出现一些其他类型的对象,它们需要相同的数据和行为,但另外还希望到处有一个额外的字段,或者一个额外的数据结构。
我们将该类命名为 Something:
public class Something {
private int id;
private String fieldA;
private String fieldB;
private List<Data> list;
// Then we have getters, setters, and some base methods
}
有时我们需要使用 SomethingElse 和 SomethingDifferent。它们 90% 都像 Something ,因为相同的数据和行为描述它们,但是它们各自还有额外的字段需要由程序的其余部分使用:
public class SomethingElse extends Something {
private String dataSpecificToSomethingElse;
// Then we have getters, setters, and some new-data specific methods
}
public class SomethingDifferentextends Something {
private List<DifferentData> dataSpecificToSomethingDifferent;
// Then we have getters, setters, and some new-data specific methods
}
我想提出一个以通用 OO 方式处理 Something 对象系列的好方法,因为我不想将应用程序的其余部分与具体实现细节结合起来(因为我们可能需要添加 SomethingWacky< /em> 稍后)。我不想直接处理子类,因为这会破坏多态性,并且可能需要向下转换/进行类型切换 - 恶心。
我能想到的解决此问题的方法如下:
- 创建一个抽象基类,定义 Something 系列的所有方法。然后,孩子们只实现他们关心的行为,为不关心的方法留下 NOP/空白覆盖。这使得一切都可以被同等对待,但会导致界面膨胀。
- 按照告诉,不要询问,通过通用辅助方法将责任转移到基类。例如,这可能是诸如 display()、doWork()、persist()、getStateFromDisplay() 等。每个子类在重写这些基本方法时都会考虑其数据。我最近读了 Allen Holub 的一篇文章,其中建议这样的做法可能是一个很好的做法。这似乎将太多外部问题的责任转移给了班级。
- 创建某种数据类,将子类中的所有额外数据/行为分组,并在Something中引用它。这感觉不太像 OO。
我在之前的项目中使用过方法 1 - 但在这种情况下,即使每个子类只实现/覆盖它所关心的方法,这些操作实际上足够通用,因此一个类可以连贯地实现全部或仅部分方法是合理的。
每种方法都在某种程度上感觉很肮脏,而且我真的不喜欢任何一种。我有什么选择?也许我完全滥用了继承或完全以错误的方式处理这个问题。我愿意接受任何建议,并希望利用面向对象技术来提出更清晰、解耦的设计。我真的很想知道人们是如何解决这样的问题的,如果您可以向我推荐任何资源,我将不胜感激。
谢谢
This is a design issue I keep running into, so I thought I would finally put it out there and see how people would approach it. The problem is as follows:
I identify a certain class that for the most part describes all instances of objects I will use, both behaviour and data-wise. That's great and works well for basic objects. Then a few other type of object crop up which need the same data and behaviour, but additionally would like to have an extra field here and there, or an extra data structure.
Let's call the class Something:
public class Something {
private int id;
private String fieldA;
private String fieldB;
private List<Data> list;
// Then we have getters, setters, and some base methods
}
Sometimes we'll need to use SomethingElse and SomethingDifferent. They are 90% like Something in that the same data and behaviour describes them, however they each additionally have extra fields which need to be used by the rest of the program:
public class SomethingElse extends Something {
private String dataSpecificToSomethingElse;
// Then we have getters, setters, and some new-data specific methods
}
public class SomethingDifferentextends Something {
private List<DifferentData> dataSpecificToSomethingDifferent;
// Then we have getters, setters, and some new-data specific methods
}
I would like to come up with a decent way to handle the Something family of objects in a generic OO manner, as I would not like to couple the rest of my application on concrete implementation details (because we might need to add SomethingWacky later on). I don't want to deal with the subclasses directly as that breaks polymorphism, and will likely include a need to downcast/do a type switch - yuck.
The approaches I can think of to resolve this are as follows:
- Create an abstract base class that defines all the methods for the Something family. Children then only implement the behaviour they are concerned with providing, leaving a NOP/blank override for methods that are not of concern. This enables everything to be treated the same, but introduces interface bloat.
- Move responsibility to the base class via generic worker methods, following Tell, Don't Ask. For example this might be things like display(), doWork(), persist(), getStateFromDisplay(), etc. Each subclass would then take into consideration its data when overriding these base methods. I read an article by Allen Holub recently that suggested something like this might be a good practice. This seems to be moving way too much responsibility for external concerns to the class.
- Create some sort of a data class that groups all of the extra data/behaviour from subclasses, and refer to that in Something. This doesn't feel very OO-like.
I've used approach 1 during a previous project - but in that instance even though each subclass only implemented/overrode the methods it cared about, the operations were actually generic enough so it was plausible that a class could coherently implement all or only some.
Each approach feels dirty in a way and I don't really like any. What are my alternatives? Perhaps I am totally misusing inheritance or approaching this the wrong way entirely. I'm open to any suggestions and would like to leverage OO techniques to come up with cleaner, decoupled designs. I'd really like to know how people have resolved issues like this, and any resources you could refer me to would be much appreciated.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当需要添加
SomethingElse
、SomethingDifferent
等时,我会问:SomethingElse
真的需要能够单独访问吗Something
的所有数据和行为元素?或者它对Something
的使用仅限于几种方法?如果是后者,通常最好封装其他类经常使用的
Something
的特定行为,然后 使用组合而不是继承。When the need to add
SomethingElse
,SomethingDifferent
, etc. crops up, I'd ask: DoesSomethingElse
really need to be able to individually access all data and behavior elements ofSomething
? Or is its use ofSomething
limited to a few methods?If it's the latter, it's usually a good idea to encapsulate the specific behavior of
Something
that is frequently used by other classes, and then use composition rather than inheritance.非常有趣的问题,不得不承认我也经常遇到同样的问题。
您所说的一切都是有效的,子类化的想法实际上是为了解决您刚才描述的问题。使用 ABC 是一个很好的面向对象原则,因此我建议您多研究一下。然而,就定义基类中的所有方法接口而言 - 您将需要重新评估。首先,我认为你应该退后一步,问一下基本的面向对象问题:所有这些行为是否都适用于该类型的对象?我还认为,根据域模型的波动性,您将预先/立即了解所有可能的行为,否则您可能不会遇到刚才描述的问题。
我认为您可以使用上述方法来重构您的继承树,并结合装饰器设计模式来解耦您的实现并为您的继承树引入稳定性,并允许通过装饰器类进行扩展。
Very interesting questions and have to admit that I do come across the same problem often too.
All you said that were all valid and the idea of subclassing is really to address problems that you just describe. Using of ABC is a good OO principle so I suggest that you look into that a bit more. However, in term of defining all the methods interface in the base class - you will need to reassess that. First I think you should take a step back and ask basic OO question about whether or not does all those behaviors apply the the object of that type? I also think that based on the volatility of your domain model, you will know all the possible behaviors upfront/right now or otherwise you probably don't have the problem you just described.
I think you can use the above approach to refactor your inheritance tree and in combination with Decorator design pattern to decouple your implementation and introduce stability to your inheritance tree and allow extensions through the decorator classes.
您可以拥有一组接口来描述应用程序中出现的不同使用场景。我想到了接口隔离原则和单一责任原则。我认为实现代码重用的更好方法是使类尽可能小且集中,并使用组合而不是继承来委托其中一些接口实现。
You could have a set of interfaces that describe the different usage scenarios that come up in your application. The interface segregation principle and the single responsibility principle come to mind. I think a better way to achieve code reuse is to keep your classes as small and focused as possible and to use composition rather than inheritance to delegate some of those interface implementations.