Vielleicht haben Sie in den letzten Monaten gerade bemerkt, dass Leute dies sagten, aber es ist guten Programmierern schon viel länger bekannt. Ich sage es mit Sicherheit seit etwa einem Jahrzehnt, wo es angebracht ist.
Der Sinn des Konzepts ist, dass die Vererbung einen großen konzeptionellen Aufwand mit sich bringt. Wenn Sie die Vererbung verwenden, enthält jeder einzelne Methodenaufruf einen impliziten Versand. Wenn Sie über tiefe Vererbungsbäume oder mehrere oder (noch schlimmer) beides verfügen, kann es zu einer königlichen PITA werden, wenn Sie herausfinden, wohin die bestimmte Methode in einem bestimmten Aufruf gesendet wird. Dadurch wird die korrekte Begründung des Codes komplexer und das Debuggen schwieriger.
Lassen Sie mich ein einfaches Beispiel zur Veranschaulichung geben. Angenommen, tief in einem Vererbungsbaum hat jemand eine Methode benannt foo
. Dann kommt jemand anderes und fügt foo
oben am Baum etwas hinzu , macht aber etwas anderes. (Dieser Fall tritt häufiger bei Mehrfachvererbung auf.) Diese Person, die in der Stammklasse arbeitet, hat die obskure untergeordnete Klasse beschädigt und erkennt sie wahrscheinlich nicht. Sie könnten eine 100-prozentige Abdeckung mit Komponententests haben und diesen Bruch nicht bemerken, da die Person an der Spitze nicht daran denkt, die untergeordnete Klasse zu testen, und die Tests für die untergeordnete Klasse nicht daran, die oben erstellten neuen Methoden zu testen . (Zugegeben, es gibt Möglichkeiten, Komponententests zu schreiben, die dies auffangen, aber es gibt auch Fälle, in denen es nicht einfach ist, Tests so zu schreiben.)
Wenn Sie dagegen Komposition verwenden, wird bei jedem Anruf in der Regel klarer, an wen Sie den Anruf weiterleiten. (OK, wenn Sie eine Invertierung der Steuerung verwenden, z. B. mit Abhängigkeitsinjektion, kann es auch problematisch sein, herauszufinden, wohin der Aufruf geht. In der Regel ist es jedoch einfacher, dies herauszufinden.) Dies erleichtert die Überlegung. Als Bonus führt die Komposition dazu, dass Methoden voneinander getrennt werden. Das obige Beispiel sollte dort nicht vorkommen, da die untergeordnete Klasse zu einer undurchsichtigen Komponente übergehen würde und es nie eine Frage gibt, ob der Aufruf von foo
für die undurchsichtige Komponente oder das Hauptobjekt bestimmt war.
Jetzt haben Sie vollkommen recht, dass Vererbung und Zusammensetzung zwei sehr unterschiedliche Werkzeuge sind, die zwei verschiedenen Arten von Dingen dienen. Sicher, Vererbung ist mit konzeptionellem Aufwand verbunden, aber wenn es sich um das richtige Tool für den Job handelt, ist der konzeptionelle Aufwand geringer als der Versuch, es nicht zu verwenden und von Hand zu tun, was es für Sie bedeutet. Niemand, der weiß, was er tut, würde sagen, dass Sie niemals Vererbung verwenden sollten. Aber sei sicher, dass es das Richtige ist.
Leider lernen viele Entwickler etwas über objektorientierte Software, lernen etwas über Vererbung und setzen ihre neue Axt so oft wie möglich ein. Das bedeutet, dass sie versuchen, Vererbung zu verwenden, wenn Komposition das richtige Werkzeug war. Hoffentlich lernen sie mit der Zeit besser, aber häufig geschieht dies erst nach ein paar entfernten Gliedmaßen usw. Wenn man ihnen vorher sagt, dass es eine schlechte Idee ist, kann dies den Lernprozess beschleunigen und Verletzungen reduzieren.