views:

315

answers:

7

I have a hierarchy of three interfaces, grandparent, parent and child. Parent and child have a method "add", which requires different input parameters in the child. While it's no problem to add the required signature in the child, the inherited method will be pointless, so is there a way to not have it in there at all? The other methods work fine.

Maybe, to achieve what I want, I can improve the design altogether, so I'll shortly outline what the interfaces are about:

I collect meter readings that consist of a time and a value. The grandparent interface is for a single reading. I also have classes that represent a number of consecutive readings (a series), and one that contains multiple series running over the same period of time (let's just call that a table).

The table can be viewed as a series (which aggregates the values orthogonally to the time axis), and both table and series can be viewed as a single reading (the implementations providing different means of aggregation), hence the inheritance. This seems to work out fine, but for the add method. (I can add a single point to the series, but for the table I need an additional parameter to tell me to which series it belongs.)

A: 

An interface is a contract. It means that anything that implements that interface will necessarily implement the methods defined. You could technically just implement it as a dummy method (no body, simply return, whatever) but to my knowledge, it must be implemented.

kgrad
+5  A: 

Maybe it would make sense to break the interface inheritance all together. Just have specific interfaces for specific types of behaviors. Whatever classes you have that implement these interfaces can just pick the ones that make sense, and won't have to worry about implementing methods that don't make sense.

Andy White
+8  A: 

No, you cannot avoid inheriting a method, since doing so would violate the Liskov substitution principle.

In practice, you could have implementations throw an UnsupportedOperationException, but that would be pretty nasty.

Can't you implement the inherited method with some sort of default value for the series?

Michael Borgwardt
+1 for the definitive answer on why this is not how interfaces work. I agree that throwing exceptions on someone trying to act according to the contract is nasty. I'll look into Andy White's suggestion for a solution.
Hanno Fietz
A: 

You can always implement the method as empty, for example:

class A implements B{ void add(A) { /*Goes Nowhere Does Nothing*/ return;} }

but really, it's not a good idea. A better solution would be for all of your grandparents, parents, and children all be the same class with two extra methods- hasParent():boolean and hasChild():boolean. This has the benefit of being a liskov substition compatible change as well as a cleaner design.

+4  A: 

The problem with inheritance is that the focus on the language mechanism makes people think about implementation rather than semantics.

When B inherits from A, it means that every instance of B is also an instance of A. In OOP, being an instance of something means typically that you should have a sensible response to its methods and at least support their messages.

If you feel that B should not support one of the messages of A, then as far as I am concerned you have two options:

BAD - Throw an "Unimplemented" exception as you would get with the collections framework. However, this is in my opinion poor form.

Good - Accept that B is not a type of A and avoid the inheritance, or restructure it (e.g., using composition and/or interfaces) so that you don't have to rewrite the code but you do not use a subtyping relation. If your application will live over time, you don't want to have semantic issues in your hierarchies.

Uri
+1 for good. As far as I understand your question, Hanno, better to use composition instead of inheritance. More details here : http://c2.com/cgi/wiki?UseCompositionAndInterfacesWithoutClassInheritance
Olivier
+1  A: 

Thanks for putting me on the right track, I upvoted the posts I found most helpful. Since my solution was inspired by the posts, but is not posted, I'll share what I decided to do:

As the hierarchy was inspired by how the data should be viewed, while the problems arise on the semantics of how you add data, I'm going to split up the interfaces for series and table into a read and a write interface each. The write interfaces have nothing to do with each other, and the read interfaces can inherit without conflicts.

I'll make this wiki, in case someone wants to expand on this.

Hanno Fietz
+1  A: 

You might want to look at the Refused Bequest code smell.

ptyx
+1 for the link, the site seems quite useful for some classic scenarios.
Hanno Fietz
I like code smells (well, the idea - not the smells themselves) - it's often easier to point what's wrong than have a theoretical debate about what's good.
ptyx