Composizione vs Ereditarietà è una delle domande di colloquio più frequenti. Devi aver anche sentito di usare la Composizione al posto dell’Ereditarietà.
Composizione vs Ereditarietà
Entrambe la composizione e l’ereditarietà sono concetti di programmazione orientata agli oggetti. Non sono legati a nessun linguaggio di programmazione specifico come Java. Prima di confrontare la composizione con l’ereditarietà programmazione, diamo una rapida definizione di entrambe.
Composizione
La composizione è la tecnica di progettazione nella programmazione orientata agli oggetti per implementare la relazione ha-una tra gli oggetti. La composizione in java si ottiene utilizzando variabili di istanza di altri oggetti. Ad esempio, una persona che ha un Lavoro viene implementata come di seguito nella programmazione orientata agli oggetti java.
package com.journaldev.composition;
public class Job {
// variabili, metodi ecc.
}
package com.journaldev.composition;
public class Person {
//la composizione ha-una relazione
private Job job;
//variabili, metodi, costruttori ecc. orientato agli oggetti
Ereditarietà
L’ereditarietà è la tecnica di progettazione nella programmazione orientata agli oggetti per implementare la relazione è-un tra gli oggetti. L’ereditarietà in Java viene implementata utilizzando la parola chiave extends. Ad esempio, la relazione “Gatto è un Animale” nella programmazione Java sarà implementata come segue.
package com.journaldev.inheritance;
public class Animal {
// variabili, metodi, ecc.
}
package com.journaldev.inheritance;
public class Cat extends Animal{
}
Composizione rispetto all’ereditarietà
Sia la composizione che l’ereditarietà promuovono il riutilizzo del codice attraverso approcci diversi. Quale scegliere? Come confrontare composizione vs ereditarietà. Avrai sentito dire che nella programmazione dovresti preferire la composizione all’ereditarietà. Vediamo alcuni dei motivi che ti aiuteranno a scegliere tra composizione ed ereditarietà.
-
L’ereditarietà è strettamente accoppiata, mentre la composizione è debolmente accoppiata. Supponiamo di avere le seguenti classi con ereditarietà.
package com.journaldev.java.examples; public class ClassA { public void foo(){ } } class ClassB extends ClassA{ public void bar(){ } }
Per semplicità, abbiamo sia la superclasse che la sottoclasse nello stesso package. Ma nella maggior parte dei casi saranno in codice separato. Potrebbero esserci molte classi che estendono la superclasse ClassA. Un esempio molto comune di questa situazione è l’estensione della classe Exception. Ora supponiamo che l’implementazione di ClassA sia cambiata come segue, aggiungendo un nuovo metodo bar().
package com.journaldev.java.examples; public class ClassA { public void foo(){ } public int bar(){ return 0; } }
Appena inizi a utilizzare la nuova implementazione di ClassA, otterrai un errore di compilazione in ClassB come
Il tipo di ritorno non è compatibile con ClassA.bar()
. La soluzione sarebbe cambiare il metodo bar() della superclasse o della sottoclasse per renderli compatibili. Se avessi usato la composizione anziché l’ereditarietà, non avresti mai affrontato questo problema. Un semplice esempio di implementazione di ClassB usando la composizione potrebbe essere il seguente.class ClassB{ ClassA classA = new ClassA(); public void bar(){ classA.foo(); classA.bar(); } }
-
Non c’è controllo degli accessi nell’ereditarietà, mentre l’accesso può essere limitato nella composizione. Esponiamo tutti i metodi della superclasse alle altre classi che hanno accesso alla sottoclasse. Quindi, se viene introdotto un nuovo metodo o ci sono falle di sicurezza nella superclasse, la sottoclasse diventa vulnerabile. Poiché nella composizione scegliamo quali metodi utilizzare, è più sicuro dell’ereditarietà. Ad esempio, possiamo fornire l’esposizione del metodo foo() della ClassA alle altre classi utilizzando il codice seguente in ClassB.
class ClassB { ClassA classA = new ClassA(); public void foo(){ classA.foo(); } public void bar(){ } }
Questo è uno dei principali vantaggi della composizione rispetto all’ereditarietà.
-
L’aggregazione offre flessibilità nell’invocazione dei metodi, il che è utile in uno scenario con più sottoclassi. Ad esempio, supponiamo di avere lo scenario di ereditarietà seguente.
abstract class Abs { abstract void foo(); } public class ClassA extends Abs { public void foo() { } } class ClassB extends Abs { public void foo() { } } class Test { ClassA a = new ClassA(); ClassB b = new ClassB(); public void test() { a.foo(); b.foo(); } }
Quindi, cosa succede se ci sono più sottoclassi? L’aggregazione renderà il nostro codice confusionario avendo un’istanza per ogni sottoclasse? No, possiamo riscrivere la classe Test come segue.
class Test { Abs obj = null; Test1(Abs o) { this.obj = o; } public void foo() { this.obj.foo(); } }
Questo ti darà la flessibilità di utilizzare qualsiasi sottoclasse in base all’oggetto utilizzato nel costruttore.
-
Un altro vantaggio della composizione rispetto all’ereditarietà è lo scopo del testing. Il testing unitario è semplice in composizione perché sappiamo quali metodi stiamo utilizzando da un’altra classe. Possiamo simulare il tutto per il testing, mentre nell’ereditarietà dipendiamo pesantemente dalla superclasse e non sappiamo quali metodi della superclasse verranno utilizzati. Quindi dovremo testare tutti i metodi della superclasse. Questo è un lavoro extra e dobbiamo farlo inutilmente a causa dell’ereditarietà.
Questo è tutto per la composizione vs ereditarietà. Hai abbastanza motivi per scegliere la composizione invece dell’ereditarietà. Utilizza l’ereditarietà solo quando sei sicuro che la superclasse non verrà modificata, altrimenti opta per la composizione.
Source:
https://www.digitalocean.com/community/tutorials/composition-vs-inheritance