Für mich ist es ein Kopplungsproblem und hängt mit der Granularität des Designs zusammen. Selbst die lockerste Form der Kopplung führt Abhängigkeiten von einer Sache zur anderen ein. Wenn das für Hunderte bis Tausende von Objekten gemacht wird, auch wenn sie alle relativ einfach sind, sich an SRP halten und selbst wenn alle Abhängigkeiten zu stabilen Abstraktionen fließen, ergibt sich eine Codebasis, die als zusammenhängendes Ganzes nur sehr schwer zu erklären ist.
Es gibt praktische Dinge, die Ihnen dabei helfen, die Komplexität einer Codebasis einzuschätzen, die in der theoretischen SE nicht häufig erörtert wird, z. B. wie tief Sie in den Aufrufstapel vordringen können, bevor Sie das Ende erreichen, und wie tief Sie gehen müssen, bevor Sie es können Mit viel Vertrauen sollten Sie alle möglichen Nebenwirkungen verstehen, die auf dieser Ebene des Aufrufstapels auftreten können, auch im Falle einer Ausnahme.
Und ich habe nur nach meiner Erfahrung festgestellt, dass flachere Systeme mit flacheren Aufrufstapeln viel einfacher zu überlegen sind. Ein extremes Beispiel wäre ein Entity-Component-System, bei dem Komponenten nur Rohdaten sind. Nur Systeme sind funktionsfähig, und bei der Implementierung und Verwendung eines ECS war es für mich das mit Abstand einfachste System überhaupt, darüber nachzudenken, wann komplexe Codebasen, die sich über Hunderttausende von Codezeilen erstrecken, auf ein paar Dutzend Systeme zusammenwachsen enthalten alle Funktionen.
Zu viele Dinge bieten Funktionalität
Als ich in früheren Codebasen gearbeitet habe, war die Alternative ein System mit Hunderten bis Tausenden von größtenteils winzigen Objekten im Gegensatz zu ein paar Dutzend sperrigen Systemen, bei denen einige Objekte nur zum Weitergeben von Nachrichten von einem Objekt an ein anderes verwendet wurden ( Message
z. B. ein Objekt , das seine hatte) eigene öffentliche Schnittstelle). Das erhalten Sie im Grunde genommen analog, wenn Sie das ECS auf einen Punkt zurücksetzen, an dem Komponenten Funktionalität haben und jede eindeutige Kombination von Komponenten in einer Entität einen eigenen Objekttyp ergibt. Und das wird tendenziell zu kleineren, einfacheren Funktionen führen, die von endlosen Kombinationen von Objekten geerbt und bereitgestellt werden, die jugendliche Ideen modellieren ( Particle
Objekt vs.Physics System
, z.B). Es kann jedoch auch zu einem komplexen Diagramm von gegenseitigen Abhängigkeiten führen, das es schwierig macht, zu überlegen, was auf breiter Ebene passiert, einfach weil es so viele Dinge in der Codebasis gibt, die tatsächlich etwas tun und daher etwas falsch machen können - - Typen, die keine "Datentypen", sondern "Objekttypen" mit zugehöriger Funktionalität sind. Typen, die als reine Daten ohne zugehörige Funktionalität dienen, können möglicherweise nichts falsch machen, da sie selbst nichts tun können.
Reine Schnittstellen helfen diesem Verständlichkeitsproblem nicht so sehr, denn selbst wenn dies die "Kompilierzeitabhängigkeiten" weniger kompliziert macht und mehr Spielraum für Änderungen und Erweiterungen bietet, werden die "Laufzeitabhängigkeiten" und Interaktionen dadurch nicht weniger kompliziert. Das Client-Objekt ruft weiterhin Funktionen für ein konkretes Kontoobjekt auf, selbst wenn diese aufgerufen werden IAccount
. Polymorphismus und abstrakte Schnittstellen haben ihre Verwendung, aber sie entkoppeln die Dinge nicht so, wie es Ihnen wirklich hilft, über alle Nebenwirkungen zu urteilen, die zu einem bestimmten Zeitpunkt auftreten können. Um diese Art der effektiven Entkopplung zu erreichen, benötigen Sie eine Codebasis, die viel weniger Funktionen enthält.
Mehr Daten, weniger Funktionalität
Daher habe ich den ECS-Ansatz, auch wenn Sie ihn nicht vollständig anwenden, als äußerst hilfreich empfunden, da er Hunderte von Objekten in reine Rohdaten mit umfangreichen, gröber konzipierten Systemen umwandelt, die all das bieten Funktionalität. Es maximiert die Anzahl der "Datentypen" und minimiert die Anzahl der "Objekttypen" und minimiert daher absolut die Anzahl der Stellen in Ihrem System, an denen tatsächlich Fehler auftreten können. Das Endergebnis ist ein sehr "flaches" System ohne komplexe Abhängigkeitsdiagramme, nur Systeme zu Komponenten, niemals umgekehrt und niemals Komponenten zu anderen Komponenten. Grundsätzlich sind es viel mehr Rohdaten und viel weniger Abstraktionen, die die Funktionalität der Codebasis auf Schlüsselbereiche, Schlüsselabstraktionen, zentralisieren und reduzieren.
30 einfachere Dinge sind nicht notwendigerweise einfacher zu überlegen als eine komplexere Sache, wenn diese 30 einfacheren Dinge miteinander zusammenhängen, während die komplexe Sache für sich allein steht. Mein Vorschlag ist also, die Komplexität von den Wechselwirkungen zwischen Objekten auf voluminösere Objekte zu übertragen, die mit nichts anderem interagieren müssen, um eine Massenentkopplung zu erreichen, und zwar auf ganze "Systeme" (wohlgemerkt nicht auf Monolithen und Gottobjekte) keine Klassen mit 200 Methoden, sondern etwas wesentlich Höheres als a Message
oder a Particle
(trotz minimalistischer Oberfläche). Bevorzugen Sie einfachere alte Datentypen. Je mehr Sie von diesen abhängig sind, desto weniger Kopplung erhalten Sie. Auch wenn dies einigen SE-Vorstellungen widerspricht, ich habe festgestellt, dass es sehr hilfreich ist.