Is it correct to give greater preference to composition than inheritance?

I have read in several articles and books of Designer patterns the expression Prefira composição em vez de herança. I believe it is a rather controversial topic due to the views discussed.

In my view, composition and inheritance are used to solve different problems, right?

class Carro {  
    private Motor motor;      
}

The above example is quite consistent because a car must have an engine. However if we replace the inheritance by composition in some cases the structure is a bit strange, for example.

class Carro {
    private Veiculo veiculo;
}

It is strange to think that vehiculo will come a car attribute. In this context, vehiculo serves only for code reuse because it does not follow a hierarchy and does not add polymorphism.

Excuse my ignorance but inherited classes maintain the coupling that is needed between them (the inheritance itself), right?

What are the advantages of using composition instead of inheritance? Is it possible to specify a right time to use them? Following the opinion of the title, the best combination would be composition + interface ?

Author: Maniero, 2014-04-02

3 answers

TL; DR - only use inheritance if the subtype can perfectly replace the base type. Also, reduce the responsibilities of your classes. Use composition to join multiple isolated responsibilities into a single object (i.e. add functionality ).

The rest of the answer is based on practical examples. For a more conceptual (and shorter) answer, including theoretical underpinning, see utluiz . And carlosrafaelgn's answer presents a rather interesting counterpoint.

When not to use inheritance?

The decision between using inheritance or not should be guided by the Liskov Substitution Principle , which says: "where an object of the base class can be used an object of the subclass should also be used". Maintaining this principle becomes complicated in complex inheritance relationships, as in the example below:

class Animal { }
class Cachorro extends Animal { }
class Gato extends Animal { }

class AbrigoAnimais {
    Animal obterAnimal() { }
    void adicionarAnimal(Animal a) { }
}
class AbrigoCachorros extends AbrigoAnimais {
    Cachorro obterAnimal() { } // OK
    void adicionarAnimal(Cachorro c) { } // É overload, não overrride
}

AbrigoAnimais canil = new AbrigoCachorros();
canil.adicionarAnimal(new Gato()); // Deveria ser válido, pelo princípio da substituição

The same it is observed in cases where the subclass is more restricted than the superclass:

class Retangulo {
    void atribuirLados(int altura, int largura) { }
}
class Quadrado extends Retangulo { }

new Quadrado().atribuirLados(10, 20); // Deveria ser válido, pelo princípio da substituição

In addition - not related to this principle, but also relevant - there is the problem of what to do in the presence of multiple inheritance:

interface Voador { }

class Veiculo { }

class Aviao extends Veiculo implements Voador { }
class Passaro extends Animal implements Voador { }

// Como reaproveitar a funcionalidade de Voador?

Or in cases where the interface is conflicting:

interface Foo {
    String metodo();
}
interface Bar {
    int metodo();
}

class Baz implements Foo, Bar {
    // Não é possivel, pois o que "metodo" vai retornar?
}

If your model doesn't have any of these problems, then go ahead and use inheritance! But most often (in my experience) at least one of these problems is present. In addition, the the programmer's motivation is often simply to "reuse code" - he doesn't have a clear taxonomy where the inheritance relationship is justified, he only uses it because he thinks he has to use it. It is in such cases that the composition becomes a preferable alternative.

How to solve using composition?

In the first case, one can do this by placing in the base type Only those methods that meet the substitution principle:

interface AbrigoAnimais {
    Animal obterAnimal();
}
class ImplAbrigoAnimais<T extends Animal> implements AbrigoAnimais {
    T obterAnimal() { } // OK, tipo de retorno covariante
    void adicionarAnimal(T animal) { } // Não está presente na interface
}
class AbrigoCachorros implements AbrigoAnimais {
    ImplAbrigoAnimais<Cachorro> abrigo = new ImplAbrigoAnimais<Cachorro>();

    Cachorro obterAnimal() { return abrigo.obterAnimal() }
    void adicionarAnimal(Cachorro cachorro) { abrigo.adicionarAnimal(cachorro); }
}

AbrigoAnimais canil = new AbrigoCachorros();
canil.obterAnimal(); // OK

Same thing in the second (note that it is can support both reading and writing):

interface Retangular {
    void atribuirLargura(int largura);
    void atribuirAltura(int altura);
}
class Retangulo implements Retangular {
    void atribuirLargura(int largura) { }
    void atribuirAltura(int altura) { }
    void atribuirLados(int largura, int altura) { } // Não está na interface
}
class Quadrado implements Retangular {
    Retangulo quadrado = new Retangulo();
    void atribuirLargura(int largura) { 
        atribuirLados(largura);
    }
    void atribuirAltura(int altura) { 
        atribuirLados(altura);
    }
    void atribuirLados(int valor) {  // Não está na interface
        quadrado.atribuirLargura(valor);
        quadrado.atribuirAltura(valor);
    }
}

Separation of responsibilities

Finally, I will demonstrate how separation of responsibilities can greatly help to model a complex set of entities:

interface ObjetoPosicionado { }
interface Animal extends ObjetoPosicionado { }
interface Veiculo extends ObjetoPosicionado { }

class ImplObjetoPosicionado implements ObjetoPosicionado {
    int x;
    int y;
    int z;
}
class ImplAnimal implements Animal {
    ObjetoPosicionado impl;
    int fome;
}
class ImplVeiculo implements Veiculo {
    ObjetoPosicionado impl;
    List<Pessoa> passageiros;
}

Here each class takes care of a specific aspect: one controls where the object is in space, another controls the specific attributes of the animal making use of the previous object (since a Animal is a ObjetoPosicionado), and another controls the attributes vehicle specifics (Ditto). With responsibilities in isolation, one can reuse them in a particular class taking advantage of only what is needed, and not wasting space:

class Cavalo implements Animal, Veiculo {
    ImplAnimal ia;
    ImplVeiculo iv;

    public Cavalo() {
        ObjetoPosicionado op = new ObjetoPosicionado(); // Uma única cópia...
        ia = new ImplAnimal(op);  // ...é utilizada aqui...
        iv = new ImplVeiculo(op); // ...e aqui.
    }

    // Realiza a interface de ObjetoPosicionado
    void deslocar(int x, int y, int z) {
        ia.deslocar(x,y,z); // Ou iv - dá no mesmo, pois ambos têm a mesma "impl"
    }

    // Realiza a interface de Animal
    void comer() {
        ia.comer();
    }

    // Realiza a interface de Veiculo
    void adicionarPassageiro(Pessoa p) {
        iv.adicionarPassageiro(p);
    }
}

But isn't that much code to write?

Yes! If the syntax of the language does not help, you would have to do a gymnastics baita to create models of this type, and the effort "pays off" afterwards (a clean API and efficient code). And what would be the solution? Leave "purity" aside and end inheritance at once :

class Cavalo {
    Animal comoAnimal() { return ia; }
    Veiculo comoVeiculo() { return iv; }
}

Cavalo c = new Cavalo();

//Animal a = c;
Animal a = c.comoAnimal();

I.e.: returns the "strangeness" of the original question, but you gain all the benefits of using composition minus the overhead of the extra code to make this possible. At the end of the day, it's still inheritance that you're doing - but without using its syntax/semantics, rather than composition.

Is that ideal?

No. The ideal would be for the computer to deal well with the "confusion" that is the way the human brain categorizes things ("birds fly. Ostriches are birds, only they do not fly. Planes also fly, but do not flap their wings. Rockets fly, and they don't even have wings! Ducks are birds, and they also swim. Boats do not swim, but that's almost it... Airplanes, rockets and boats are vehicles. Missiles look like rockets, but they do not carry passengers.").

In the absence of this, it remains only to create models with desirable characteristics, such as type safety. It aims to ensure that "like-minded" entities can be treated as a single type - by substitution according to the principle cited at the beginning of the answer. If you don't have that security, what's the point of having an object that can or can't fulfill the class contract?

In the end, what matters to the programmer is that a certain object has a certain desirable functionality. That is, conceptually um carro é um veículo, but pragmatically um objeto carro possui a funcionalidade de um objeto veículo.

 63
Author: mgibsonbr, 2020-06-11 14:45:34

I know I can enter the sphere of opinion, but still, very curious the placement

Prefer composition instead of inheritance

During my master's classes, I heard the exact opposite. Among all types of inheritance that exist, those that respect the principle of substitution were "greenish" and those that do not respect, were "Redish".

In a nutshell, the principle of replacement was passed to me as follows:

  • Let be the classes A and B, B subclass of A

  • You can replace an instance iA of Class A with an instance iB of Class B in any situation, with no observable effect

From there, the concept of subclass and subtype was defined:

  • A subclass is a structure built using inheritance, satisfying or not the substitution principle

  • A subtype is a class that satisfies the principle of substitution

Therefore, not all subclasses are subtypes, and (in some rather specific languages) it is possible to have a subtype that is not a subclass.

With this defined, it is possible to separate which forms of inheritance may or may not respect the principle of substitution.

Some that may respect the principle:

  • specialization
  • specification
  • extension
  • combination (or Multiple Inheritance)

While some others do not always respect (and should be avoided if possible):

  • Construction: a superclass is used only by its behavior, and has no real relationship to the superclass. For example, create a class Pilha, which is a subclass of Vetor, just because some of the necessary methods are already there ready in class Vetor.
  • generalization: a subclass extends the behavior of the superclass by creating a more general type of object, generalizing or extending the superclass by providing more functionality.
  • limitation: the subclass limits some part of the superclass's behavior by overlaying undesirable methods (for example, causing a functional superclass method to throw an exception like InvalidOperationException).
  • variation: two or more classes they seem to be related, but it is not clear who should be superclass and who should be subclass. It usually occurs when building classes from other existing classes. For example, between classes Mouse, TouchPad and Joystick, it becomes difficult, or even impossible, to determine who should be superclass of whom in theory. But in practice, the developer simply decides that Mouse is a particular case of Joystick, and that's it!

With that said, it is possible to leave effectively for the idea of composition.

Clearly, in the example of "inheritance by construction", where Pilha is a subclass of Vetor, the use of the sentence Stack is-a vector sounds very bad. In this case, the composition should have been used, since the sentence stack has-a vector sounds much better (considering that the listening person knows what a stack is and what a vector is).

On the other hand, when using composition in cases where inheritance could be used, the developer loses several possibilities. For example, if a method m1() expects a Class A, and you created a class B (which, although logically is-a Class A and behaves as such, was created by composition rather than inheritance), you will not be able to pass an instance iB to m1(), and you will need to create another method m2() to work with instances of the Class B. That is, you missed an opportunity to reuse code, and it's over "duplicating" code.

Code reuse and behavior reuse bring several benefits and safeguards:

  • leverages the creation of new software components from existing software components
  • productivity (less time spent developing new components)
  • consistency (a certain behavior stays equal/consistent throughout the system)
  • reliability (reusing already tested and validated Code provides extra security for the Developer)

Both composition and inheritance reuse code and/or behavior, and both have their specific usage scenarios. After all that I went through during the master's degree, and my professional life, I would not say that one is better than the other, because the scenarios of use of each are different.

A "thumb rule" for this case: if the sentence A is-a b "sounds good", then you should use inheritance, to enjoy everything it offers. Otherwise, both if A is-a b does not" sound good", but you want to reuse the behavior of B in A, and if A has-a b" sounds good", then you should use composition.

 34
Author: carlosrafaelgn, 2014-06-19 16:43:47

less verbose Answer ;)

Many use inheritance incorrectly to reuse or organize code.

Inheritance

Extend a class only when it is necessary to replace the original class with the subclass in order to extend the original functionality.

This involves the Liskov Substitution Principle and the Open / Closed Principle of the concept of design SOLID .

Basically, the Substitution Principle says that a subclass should replace the superclass without any change in the existing code.

The closed/Open principle says that a class must be closed for change and open for extension, which means that it must allow new functionality without changing existing code.

Composition and delegation

Use composition and delegation to distribute responsibilities properly between classes. A class can reference other classes and then delegate the execution of methods that are not its responsibility to them.

This has to do with the principle of Single Responsibility .

Polymorphism

To allow polymorphism, use interfaces and apply the principle of Interface Segregation. This means that no class should be forced to rely on methods it does not use.

As interfaces should not simply contain the methods that we find intuitive at a given time, because this concept is different in each context (as well explained in Marcelo's answer). Before, an interface should contain only those methods that are interesting to those who use it. If it is used in two different situations, divide it into two.

 25
Author: utluiz, 2017-04-13 12:59:32