Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

interface vs composition

I think I understand the difference between interface and abstract. Abstract sets default behavior and in cases of pure abstract, behavior needs to be set by derived class. Interface is a take what you need without the overhead from a base class. So what is the advantage of interface over composition? The only advantage I can think is use of protected fields in the base class. What am I missing?

like image 627
Aaron Avatar asked Dec 01 '22 00:12

Aaron


2 Answers

Your title does not make sense, and your explanations are a bit blurry, so let's define the terms (and introduce the key missing one).

There are two different things going on here:

  • Abstract Class vs Interface
  • Inheritance vs Composition

Let us start with Interfaces and Abstract Classes.

  • An Abstract Class (in C++) is a class which cannot be instantiated because at least one its method is a pure virtual method.
  • An Interface, in Java-like languages, is a set of methods with no implementation, in C++ it is emulated with Abstract Classes with only pure virtual methods.

So, in the context of C++, there is not much difference between either. Especially because the distinction never took into account free-functions.

For example, consider the following "interface":

class LessThanComparable {
public:
    virtual ~LessThanComparable() {}

    virtual bool less(LessThanComparable const& other) const = 0;
};

You can trivially augment it, even with free functions:

inline bool operator<(LessThanComparable const& left, LessThanComparable const& right) {
    return left.less(right);
}

inline bool operator>(LessThanComparable const& left, LessThanComparable const& right) {
    return right.less(left);
}

inline bool operator<=(LessThanComparable const& left, LessThanComparable const& right) {
    return not right.less(left);
}

inline bool operator>=(LessThanComparable const& left, LessThanComparable const& right) {
    return not left.less(right);
}

In this case, we provide behavior... yet the class itself is still an interface... oh well.


The real debate, therefore, is between Inheritance and Composition.

Inheritance is often misused to inherit behavior. This is bad. Inheritance should be used to model a is-a relationship. Otherwise, you probably want Composition.

Consider the simple use case:

class DieselEngine { public: void start(); };

Now, how do we build a Car with this ?

If you inherit, it will work. However, suddenly you get such code:

void start(DieselEngine& e) { e.start(); }

int main() {
    Car car;
    start(car);
}

Now, if you decide to replace DieselEngine with WaterEngine, the above function does not work. Compilation fails. And having WaterEngine inherit from DieselEngine certainly feels ikky...

What is the solution then ? Composition.

class Car {
public:
    void start() { engine.start(); }

private:
    DieselEngine engine;
};

This way, noone can write nonsensical code that assumes that a car is an engine (doh!). And therefore, changing the engine is easy with absolutely no customer impact.

This means that there is less adherence between your implementation and the code that uses it; or as it is usually referred to: less coupling.


The rule of thumb is that in general, inheriting from a class which has data or implement behavior should be frown upon. It can be legitimate, but there are often better ways. Of course, like all rule of thumb, it is to be taken with a grain of salt; be careful of overengineering.

like image 125
Matthieu M. Avatar answered Dec 04 '22 16:12

Matthieu M.


An interface defines how you will be used.

You inherit in order to be reused. This means you want to fit into some framework. If you don't need to fit into a framework, even one of your own making, don't inherit.

Composition is an implementation detail. Don't inherit in order to get the implementation of the base class, compose it. Only inherit if it allows you to fit into a framework.

like image 40
Peter Wood Avatar answered Dec 04 '22 15:12

Peter Wood