Random thought on OOP -- Prefer composition over inheritance

Let's see what's inheritance and composition. Just show by example, not accurate definition.
Assume there are 3 features (modules), ModuleA, ModuleB, ModuleC. Two classes ClassA and ClassB want to use them. ClassA will use ModuleA and ModuleB, ClassB will use all three features. And we also assume for current moment all A, B and C modules will be only used by class ClassA and ClassB.

Inheritance:

class ClassA {
	functions of ModuleA;
	functions of ModuleB;
};

class ClassB extends ClassA {
	functions of ModuleC;
};

Composition:

class ModuleA {};
class ModuleB {};
class ModuleC {};

class ClassA {
	ModuleA ma;
	ModuleB mb;
};

class ClassB {
	ModuleA ma;
	ModuleB mb;
	ModuleC mc;
};

//Alternative:

class ClassB {
	ClassA ca;
	ModuleC mc;
};

 

Both approaches reuse the modules.
In composition, we usually call the modules "components".
In game development, this kind of composition is also known as "component based entity system".

Benefits we can get from composition than inheritance:

  1. Composition allows splitting the problem domain to different independent child components. Each component can be developed independently by different developers. We can have three programmers to develop ModuleA, ModuleB and ModuleC simultaneously.
  2. Component can be replaced, removed, or added at runtime (dynamic binding). On the contrary, inheritance can't or very hard (depending on the programming language) to be replaced, removed, or added features at runtime (static binding).
  3. Composition is much less coupling than inheritance. Inheritance forces coupling between ClassB and ClassA while composition doesn't.
  4. Component can be reused in other composition to construct different behaviors. There is no way to only reuse a single feature in inheritance. Inheritance is forced to bring parent's all features to child.
  5. Less redundancy. Inheritance tends to lead redundancy. B inherits from A, usually B only needs partial features of A, the not needed part becomes redundancy. If B is composed by reusing components from A, B can abandon the unused components to avoid redundancy.
  6. Better encapsulation. Composition is only based on public interface. Each component doesn't know other's internal detail. That's what encapsulation exactly means. While inheritance exposes the super class' internal details (the protected interface) to sub class. And even worse, sub class always has some presuppositions about the super class, and super class also supposes child class can respect any convention it required.
  7. Easy to modify. In composition, any component can be changed with very small impacting to other components as long as keeping the public interface. In inheritance, it's forced a tight coupled hierarchy chain. Any changes to any node of the chain may influence the whole chain.
  8. Small modules, atomic modules. In composition, a bigger module is composed with smaller modules so the module levels are controllable. It's possible that we can have only two levels, the atomic modules, and the real functional modules, so the hierarchy is quite plain. In inheritance, a bigger module is made by inheriting from smaller module. Want a module, inherit, then get one more level. Then there may be a lot of levels, making the application module hierarchy really complicated and hard to understand.

For "is a" relationship we should use inheritance? Yes and no.

Though we were taught that inheritance is for "is a" and composition is for "has a", in many points "is a" can be understood as "has a". For example, programmer is a kind of person, so programmer is inherited from person, but if we think programmer is a kind of person that has the character which called "programming job", programmer can be composed with person and "programming job".

Disadvantage of composition:

The only one disadvantage may be we will finally get a lot of small modules (classes). This is hardly a disadvantage, and under some other view, this is really an advantage. For C++, a lot of small classes really don't matter, won't bloat the code, nor slow down the execution. For Java, C#, Objective C, this may bloat the code. But unless you are working on resource limited device, this indeed is not a big problem.

When to use Composition:

  1. When ClassB is using ClassA rather than implementing ClassA.
  2. When the components may be changed (added, replaced, or removed).
  3. Even if the ClassB and ClassA have an "is a" relationship for now, if the relationship may be changed in the future, we should use composition instead of inheritance. A programmer is a person, but he may choose any other job in the future, such as a Stock broker. If Programmer is inherited from Person, we may have trouble after the job change. So use composition. Programmer is just a person, which has a job title of programmer.
  4. Whenever inheritance is not a must. Yes, prefer composition over inheritance.

When to use Inheritance:

  1. When ClassB is implementing ClassA rather than using ClassA. For example, ClassA may be an abstract dialog with polymorphism functions, such as show, hide, etc. ClassB is a file dialog that needs to implement the missed functions (show, hide).
  2. When ClassB and ClassA have clear "is a" relationship and the relationship will not change at runtime. That's to say, the bind is always static. Though cat can be considered as a kind of object that has an animal character and cat character, the both characters will never change for a cat. Replacing the "cat character" with "dog character" will produce a dog, not a cat. So the binding is static, and the relationship is a kind of fixed "is a", so it's nature that cat is inherited from animal.
  3. When the interface of ClassA is also the interface of ClassB. This really depends. With composition, the ClassB can rewrite proxy interface to redirect to the internal ClassA.
  4. When ClassB will be used where ClassA is expected. That implies ClassB "is a" ClassA. However, in languages that support type level polymorphism (generic programming), such as C++, it's possible to omit the need of inheritance by using templates.


Summary:

I showed why we should prefer composition over inheritance. And if you google "composition inheritance" (without quote mark), millions of web pages are there to support the opinion. But that doesn't mean in the "composition vs. inheritance" battle composition is the winner. No. It's not a battle at all. Choose the appropriate method for appropriate problem, keep it nature and logical. Nothing is absolute good or bad, correct or wrong.

Write a comment

  • Required fields are marked with *
  • Security Code is case sensitive, 'A' is different with 'a'.

If you have trouble reading the code, click on the code itself to generate a new random code.
Security Code: