Reputation: 3265
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:
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
Upvotes: 6
Views: 244
Reputation: 56467
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.
Upvotes: 0
Reputation: 4365
When the need to add SomethingElse
, SomethingDifferent
, etc. crops up, I'd ask: Does SomethingElse
really need to be able to individually access all data and behavior elements of Something
? Or is its use of Something
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.
Upvotes: 3
Reputation: 6465
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.
Upvotes: 1