tags:

views:

1088

answers:

8

I will choose Java as an example, most people know it, though every other OO language was working as well.

Java, like many other languages, has interface inheritance and implementation inheritance. E.g. a Java class can inherit from another one and every method that has an implementation there (assuming the parent is not abstract) is inherited, too. That means the interface is inherited and the implementation for this method as well. I can overwrite it, but I don't have to. If I don't overwrite it, I have inherited the implementation.

However, my class can also "inherit" (not in Java terms) just an interface, without implementation. Actually interfaces are really named that way in Java, they provide interface inheritance, but without inheriting any implementation, since all methods of an interface have no implementation.

Now there was this article, saying it's better to inherit interfaces than implementations, you may like to read it (at least the first half of the first page), it's pretty interesting. It avoids issues like the fragile base class problem. So far this makes all a lot of sense and many other things said in the article make a lot of sense to me.

What bugs me about this, is that implementation inheritance means code reuse, one of the most important properties of OO languages. Now if Java had no classes (like James Gosling, the godfather of Java has wished according to this article), it solves all problems of implementation inheritance, but how would you make code reuse possible then?

E.g. if I have a class Car and Car has a method move(), which makes the Car move. Now I can sub-class Car for different type of cars, that are all cars, but are all specialized versions of Car. Some may move in a different way, these need to overwrite move() anyway, but most would simply keep the inherited move, as they move alike just like the abstract parent Car. Now assume for a second that there are only interfaces in Java, only interfaces may inherit from each other, a class may implement interfaces, but all classes are always final, so no class can inherit from any other class.

How would you avoid that when you have an Interface Car and hundred Car classes, that you need to implement an identical move() method for each of them? What concepts for code reuse other than implementation inheritance exist in the the OO world?

Some languages have Mixins. Are Mixins the answer to my question? I read about them, but I cannot really imagine how Mixins would work in a Java world and if they can really solve the problem here.

Another idea was that there is a class that only implements the Car interface, let's call it AbstractCar, and implements the move() method. Now other cars implement the Car interface as well, internally they create an instance of AbstractCar and they implement their own move() method by calling move() on their internal abstract Car. But wouldn't this be wasting resources for nothing (a method calling just another method - okay, JIT could inline the code, but still) and using extra memory for keeping internal objects, you wouldn't even need with implementation inheritance? (after all every object needs more memory than just the sum of the encapsulated data) Also isn't it awkward for a programmer to write dummy methods like

public void move() {
    abstractCarObject.move();
}

?

Anyone can imagine a better idea how to avoid implementation inheritance and still be able to re-use code in an easy fashion?

+3  A: 

You could also use composition and the strategy pattern.link text

public class Car
{
  private ICar _car;

  public void Move() {
     _car.Move();
  }
}

This is far more flexible than using inheritance based behaviour as it allows you to change at runtime, by substituting new Car types as required.

Xian
+2  A: 

You can use composition. In your example, a Car object might contain another object called Drivetrain. The car's move() method could simply call the drive() method of it's drivetrain. The Drivetrain class could, in turn, contain objects like Engine, Transmission, Wheels, etc. If you structured your class hierarchy this way, you could easily create cars which move in different ways by composing them of different combinations of the simpler parts (i.e. reuse code).

Ben Hoffstein
A: 

You should read Design Patterns. You will find that Interfaces are critical to many types of useful Design Patterns. For example abstracting different types of network protocols will have the same interface (to the software calling it) but little code reuse because of different behaviors of each type of protocol.

For some algorithms are eye opening in showing how to put together the myriad elements of a programming to do some useful task. Design Patterns do the same for objects.Shows you how to combine objects in a way to perform a useful task.

Design Patterns by the Gang of Four

RS Conley
+2  A: 

To make mixins/composition easier, take a look at my Annotations and Annotation Processor:

http://code.google.com/p/javadude/wiki/Annotations

In particular, the mixins example:

http://code.google.com/p/javadude/wiki/AnnotationsMixinExample

Note that it doesn't currently work if the interfaces/types being delegated to have parameterized methods (or parameterized types on the methods). I'm working on that...

Scott Stanchfield
This is some pretty interesting stuff, you have there. I will take a detailed look at it :)
Mecki
+6  A: 

Short answer: Yes it is possible. But you have to do it on purpose and no by chance ( using final, abstract and design with inheritance in mind, etc. )

Long answer:

Well, inheritance is not actually for "code re-use", it is for class "specialization", I think this is a misinterpretation.

For instance is it a very bad idea to create a Stack from a Vector, just because they are alike. Or properties from HashTable just because they store values. See [Effective].

The "code reuse" was more a "business view" of the OO characteristics, meaning that you objects were easily distributable among nodes; and were portable and didn't not have the problems of previous programming languages generation. This has been proved half rigth. We now have libraries that can be easily distributed; for instance in java the jar files can be used in any project saving thousands of hours of development. OO still has some problems with portability and things like that, that is the reason now WebServices are so popular ( as before it was CORBA ) but that's another thread.

This is one aspect of "code reuse". The other is effectively, the one that has to do with programming. But in this case is not just to "save" lines of code and creating fragile monsters, but designing with inheritance in mind. This is the item 17 in the book previously mentioned; Item 17: Design and document for inheritance or else prohibit it. See [Effective]

Of course you may have a Car class and tons of subclasses. And yes, the approach you mention about Car interface, AbstractCar and CarImplementation is a correct way to go.

You define the "contract" the Car should adhere and say these are the methods I would expect to have when talking about cars. The abstract car that has the base functionality that every car but leaving and documenting the methods the subclasses are responsible to handle. In java you do this by marking the method as abstract.

When you proceed this way, there is not a problem with the "fragile" class ( or at least the designer is conscious or the threat ) and the subclasses do complete only those parts the designer allow them.

Inheritance is more to "specialize" the classes, in the same fashion a Truck is an specialized version of Car, and MosterTruck an specialized version of Truck.

It does not make sanse to create a "ComputerMouse" subclase from a Car just because it has a Wheel ( scroll wheel ) like a car, it moves, and has a wheel below just to save lines of code. It belongs to a different domain, and it will be used for other purposes.

The way to prevent "implementation" inheritance is in the programming language since the beginning, you should use the final keyword on the class declaration and this way you are prohibiting subclasses.

Subclassing is not evil if it's done on purpose. If it's done uncarefully it may become a nightmare. I would say that you should start as private and "final" as possible and if needed make things more public and extend-able. This is also widely explained in the presentation"How to design good API's and why it matters" See [Good API]

Keep reading articles and with time and practice ( and a lot of patience ) this thing will come clearer. Although sometime you just need to do the work and copy/paste some code :P . This is ok, as long you try to do it well first.

Here are the references both from Joshua Bloch ( formerly working in Sun at the core of java now working for Google )


[Effective] Effective Java. Definitely the best java book a non beginner should learn, understand and practice. A must have.

Effective Java


[Good API]Presentation that talks on API's design, reusability and related topics. It is a little lengthy but it worth every minute.

How To Design A Good API and Why it Matters

Regards.


Update: Take a look at minute 42 of the video link I sent you. It talks about this topic:

"When you have two classes in a public API and you think to make one a subclass of another, like Foo is a subclass of Bar, ask your self , is Every Foo a Bar?... "

And in the minute previous it talks about "code reuse" while talking about TimeTask.

OscarRyz
Seriously, I fail to see how a truck should inherit from a car :)
Trap
Maybe Trucks and Cars should inherit from Automobile. :-)
StriplingWarrior
+3  A: 

The problem with most example against inheritance are examples where the person is using inheritance incorrectly, not a failure of inheritance to correctly abstract.

In the article you posted a link to, the author shows the "brokenness" of inheritance using Stack and ArrayList. The example is flawed because a Stack is not an ArrayList and therefore inheritance should not be used. The example is as flawed as String extending Character, or PointXY extending Number.

Before you extend class, you should always perform the "is_a" test. Since you can't say Every Stack is an ArrayList without being wrong in some way, then you should not inheirit.

The contract for Stack is different than the contract for ArrayList (or List) and stack should not be inheriting methods that is does not care about (like get(int i) and add()). In fact Stack should be an interface with methods such as:

interface Stack<T> {
   public void push(T object);
   public T pop();
   public void clear();
   public int size();
}

A class like ArrayListStack might implement the Stack interface, and in that case use composition (having an internal ArrayList) and not inheritance.

Inheritance is not bad, bad inheritance is bad.

Laplie
A: 

Inheritance is not necessary for an object oriented language.

Consider Javascript, which is even more object-oriented than Java, arguably. There are no classes, just objects. Code is reused by adding existing methods to an object. A Javascript object is essentially a map of names to functions (and data), where the initial contents of the map is established by a prototype, and new entries can be added to a given instance on the fly.

erickson
+2  A: 

It's funny to answer my own question, but here's something I found that is pretty interesting: Sather.

It's a programming language with no implementation inheritance at all! It knows interfaces (called abstract classes with no implementation or encapsulated data), and interfaces can inherit of each other (actually they even support multiple inheritance!), but a class can only implement interfaces (abstract classes, as many as it likes), it can't inherit from another class. It can however "include" another class. This is rather a delegate concept. Included classes must be instantiated in the constructor of your class and are destroyed when your class is destroyed. Unless you overwrite the methods they have, your class inherits their interface as well, but not their code. Instead methods are created that just forward calls to your method to the equally named method of the included object. The difference between included objects and just encapsulated objects is that you don't have to create the delegation forwards yourself and they don't exist as independent objects that you can pass around, they are part of your object and live and die together with your object (or more technically spoken: The memory for your object and all included ones is created with a single alloc call, same memory block, you just need to init them in your constructor call, while when using real delegates, each of these objects causes an own alloc call, has an own memory block, and lives completely independently of your object).

The language is not so beautiful, but I love the idea behind it :-)

Mecki