Tuesday, November 30, 2010

Using inheritance

Using inheritance for coupling classes is very often used by php developers. At the first glance it seems to be great choice. Subclasses can refer to the members of their parent class as if it were defined in the body of the subclass. The protected keyword is explicitly designed for this kind of usage. Abstract classes are fancy and the abstract keyword is also introduced to enforce the usage of inheritance. You may think that if you design an API, that uses abstract classes designed to be extended by the users of the API then you make it extremely flexible since it can be extended by anybody and it's methods can be implemented or overriden by anybody. I also often see that people don't use private members but they use protected everywere, saying it's more maintainable (no change is needed if later on the class will possibly be extended). But I think inheritance is over-used by most php developers and it's a bad habit.





In fact I think that using inheritance all the time is a very big mistake and makes your code hard to maintain and more hard to integrate (the latter is a mistake for application codes and a fatal mistake for libraries). The main problem with inheritance is that if you use it for coupling two classes, then a very important property of the subclass is used up: it's superclass. It is taken, reserved, and it can not be used for anything else furthermore. If you want to connect your subclass to an other class using inheritance, you can't. PHP does not support multiple inheritance (and it's not a problem). Let's imagine that Library A can be used by extending it's class ClassA and doing some stuff with protected members. Then Library B can be used by extending it's ClassB and doing some stuff with it's protected members. How to use both libraries in the same class? There is no way to do it.

A worse case is when a library (or API) requires it's client ClassC to extend the library class ClassA, but ClassA is empty or maybe it contains some irrelevant code. Then when the library instantiates ClassC then it checks if it is an instance of ClassA, and if not then an exception is thrown and you fail to use the library. It is a pretty widely used design and it is totally bad: the superclass of ClassC is reserved by the library, it's used up, and used for nothing.

An other problem with inheritance is that it is sometimes very hard to debug. Imagine that a class calls a method of it's 5th parent class and you want to look at the body of that method. If your IDE can't find it on it's own, then you do the following: you open the source file of superclass, search for the method implementation, you don't find it, you go on with the next parent class and start the process again. You can waste a lot of time with it.

A code that uses inheritance all the time can look pretty at first time. A complex class hierarchy tree looks to be well designed and it is obviously comes with smaller codebase and less duplicate code (more about it later in this post). But it's not flexible. When it comes to maintenance and requirements change, it can be very hard to change the inheritance hierarchy, and massive, error-prone refactoring comes.

To sum up, please try not using inheritance. Use it only if there is no other solution for your problem and before using it, think about interfaces and composition. A very typical mistake is creating an abstract class that contains only abstract methods. These classes should obviously be interfaces instead.

Interfaces are much more flexible. A class is enabled to implement any quantity of interfaces therefore there is no problem like the one and only superclass issue I mentioned above. Interfaces are also types so you can use them for method parameter constraints and in instanceof expressions if you want.

Composition is a very basic method: if ClassA has a member with type ClassB then ClassB is a component of ClassA. ClassA instances can use the members of a ClassB instance via the member. Composition is also a flexible method for coupling (ClassA can have any quantity of members). Sadly you can't explicitly define a type constraint for class members, but a workaround can be setting it's visibility to private, creating a setter method for it and you can define a type constraint for the setter method parameter.

It's a good practice to use interfaces for type constraints for setter methods (otherwise for any other methods too). This way you can make sure that your member will have the methods required by your class but you don't have to care about that how they are implemented. This is called loose coupling.

In conclusion:
  • try to avoid using inheritance
  • use interfaces
  • use composition
  • use interfaces and compositon together and your classes will be loosely coupled

Finally some common cases when people use inheritance when they shuldn't:

In some cases abstract classes are used because it's subclasses are very similar and it's worth creating some concrete methods in the abstract class to be used by the subclasses to avoid duplicate code. You can ask that what to do in this case with interfaces. I think you still have to create one. When you design your code first think in interfaces. If you find that an interface will have very similar implementations then create an abstract implementation of the interface with some commonly used concrete methods. And never use the abstract class for type constraints but use the interface instead. This way you can avoid duplicate code but your design will be still flexible.

Sometimes you will find that you have the same members in different implementations of an interface. In such cases people often turn their interface into an abstract class and define the common class member there to avoid duplicate code. It's a bad habit. Yes, it's true that having members (in different classes) with the same type used for the same thing is duplicate code, and duplicate code is evil. But duplicate code is evil because it's hard to maintain - and an unflexible code architecture is much more hard to maintain then some minimal code duplication (I mean the similar members).

3 comments:

  1. Why would you try to superclass a library?
    You would have to modify the source of the library to do that, and that should be the last thing that you should do some other folks code.
    If thats some third party library, then you would have to merge your changes with every upgrade.
    If it's from your own project, then you can create a class, which extends/implements your modifications.

    Inheritance is a great feature, because thats is the major building block for the polymorphism, which is one of the most important part of the OOP paradigm.

    So use inheritance/abstract classes for code reuse, and interfaces for expose/enforce your class boundaries.

    Tyrael

    ReplyDelete
  2. hi,

    about libraries: it's clear that modifying the source of a 3rdparty lib in an application is an odd thing, but designing a library that has an API that can be used by extending library classes is a mistake.

    ReplyDelete
  3. why do you think so?
    http://en.wikipedia.org/wiki/Decorator_pattern

    Tyrael

    ReplyDelete