Das Prinzip der Abhängigkeitsumkehr. Ein kritischer Blick auf das Abhängigkeitsinversionsprinzip

heim / Geschäftspläne

Tatsächlich alle Prinzipien SOLIDE Sie sind eng miteinander verbunden und ihr Hauptziel ist es, bei der Entwicklung hochwertiger, skalierbarer Software mitzuhelfen. Aber das letzte Prinzip SOLIDE hebt sich wirklich von ihrem Hintergrund ab. Schauen wir uns zunächst die Formulierung dieses Prinzips an. Also, Prinzip der Abhängigkeitsumkehr (Abhängigkeitsinversionsprinzip – DIP): „Abhängigkeit von Abstraktionen. Es besteht keine Abhängigkeit von etwas Bestimmtem.“. Auch der bekannte Spezialist auf dem Gebiet der Softwareentwicklung, Robert Martin, betont das Prinzip TAUCHEN und stellt es einfach als Ergebnis der Befolgung anderer Prinzipien dar SOLIDE— das Offen/Geschlossen-Prinzip und das Liskov-Substitutionsprinzip. Denken Sie daran, dass der erste besagt, dass die Klasse nicht geändert werden sollte, um neue Änderungen einzuführen, und der zweite die Vererbung betrifft und die sichere Verwendung abgeleiteter Typen eines Basistyps voraussetzt, ohne den korrekten Betrieb des Programms zu beeinträchtigen. Robert Martin hat dieses Prinzip ursprünglich wie folgt formuliert:

1). Module auf höheren Ebenen sollten nicht von Modulen auf niedrigeren Ebenen abhängen. Module auf beiden Ebenen müssen von Abstraktionen abhängen.

2). Abstraktionen sollten nicht von Details abhängen. Details müssen von Abstraktionen abhängen.

Das heißt, Klassen müssen im Hinblick auf Abstraktionen entwickelt werden und nicht auf der Grundlage ihrer konkreten Implementierungen. Und wenn Sie die Prinzipien befolgen OCP Und LSP, dann ist es genau das, was wir erreichen werden. Gehen wir also noch einmal kurz auf die Lektion zurück. Dort haben wir uns als Beispiel die Klasse angesehen Barde, die zu Beginn streng an die Klasse gebunden war Gitarre, repräsentiert ein bestimmtes Musikinstrument:

öffentliche Klasse Bard ( private Gitarrengitarre; öffentliche Bard(Gitarrengitarre) ( this.guitar = Gitarre; ) öffentliches void play() ( Guitar.play(); ) )

öffentliche Klasse Barde (

private Gitarre; Gitarre;

öffentlicher Barde (Gitarre)

Das. Gitarre = Gitarre ;

öffentliches leeres Spiel()

Gitarre spielen();

Wenn wir dieser Klasse Unterstützung für andere Musikinstrumente hinzufügen wollten, müssten wir diese Klasse auf die eine oder andere Weise modifizieren. Dies ist ein klarer Verstoß gegen den Grundsatz OCP. Und Sie haben vielleicht schon gemerkt, dass es sich dabei auch um Verstöße gegen den Grundsatz handelt TAUCHEN, da sich in unserem Fall herausstellte, dass unsere Abstraktion von Details abhängig war. Unter dem Gesichtspunkt des weiteren Ausbaus unserer Klasse ist das überhaupt nicht gut. Damit unsere Klasse die Bedingungen des Prinzips erfüllt OCP Wir haben dem System eine Schnittstelle hinzugefügt Instrument, die von bestimmten Klassen implementiert wurde, die bestimmte Arten von Musikinstrumenten repräsentieren.

Datei Instrument.java:

öffentliche Schnittstelle Instrument ( void play(); )

öffentliche Schnittstelle Instrument (

void play();

Datei Gitarre.java:

Klasse Guitar implementiert Instrument( @Override public void play() ( System.out.println("Play Guitar!"); ) )

Klasse Gitarre implementiert Instrument (

@Override

öffentliches leeres Spiel()

System. aus . println("Gitarre spielen!");

Datei Lute.java:

öffentliche Klasse Lute implementiert Instrument( @Override public void play() ( System.out.println("Play Lute!"); ) )

öffentliche Klasse Lute implementiert Instrument (

@Override

öffentliches leeres Spiel()

System. aus . println("Laute spielen!");

Danach haben wir die Klasse gewechselt Barde, sodass wir bei Bedarf Implementierungen durch genau die ersetzen können, die wir benötigen. Dies bringt zusätzliche Flexibilität in das erstellte System und reduziert seine Kopplung (starke Abhängigkeiten der Klassen voneinander).

public class Bard ( privates Instrument instrument; public Bard() ( ) public void play() ( instrument.play(); ) public void setInstrument(Instrument instrument) ( this.instrument = instrument; ) )

öffentliche Klasse Barde (

privates Instrument Instrument;

, Pluralität Schnittstellen und Abhängigkeitsinversion. Fünf agile Prinzipien, die Sie jedes Mal leiten sollten, wenn Sie Code schreiben.

Es wäre unfair, Ihnen zu sagen, dass eines der SOLID-Prinzipien wichtiger ist als das andere. Allerdings hat vielleicht kein anderes Prinzip einen so unmittelbaren und tiefgreifenden Einfluss auf Ihren Code wie das Dependency Inversion Principle, kurz DIP. Wenn Sie Schwierigkeiten haben, andere Prinzipien zu verstehen und anzuwenden, sollten Sie mit diesem beginnen und dann den Rest auf Code anwenden, der bereits dem Prinzip der Abhängigkeitsumkehr folgt.

Definition

A. Module auf höherer Ebene sollten nicht von Modulen auf niedrigerer Ebene abhängen. Sie alle müssen auf Abstraktionen beruhen.
B. Abstraktionen sollten nicht von Details abhängen. Details müssen von Abstraktionen abhängen.

Wir sollten uns bemühen, unseren Code auf der Grundlage dieser Zahlen zu organisieren. Hier sind einige Techniken, die dabei helfen können. Die maximale Länge von Funktionen sollte nicht mehr als vier Zeilen betragen (fünf inklusive Titel), damit sie vollständig in unser Gedächtnis passen. Einrückungen sollten nicht tiefer als fünf Ebenen sein. Klassen mit nicht mehr als fünf Methoden. In Entwurfsmustern liegt die Anzahl der verwendeten Klassen typischerweise zwischen fünf und neun. Unsere obige High-Level-Architektur enthält vier bis fünf Konzepte. Es gibt fünf SOLID-Prinzipien, für die jeweils fünf bis neun Konzepte/Module/Klassen als Beispiele erforderlich sind. Die ideale Größe eines Entwicklungsteams liegt zwischen fünf und neun. Die ideale Anzahl an Teams in einem Unternehmen liegt ebenfalls zwischen fünf und neun.

Wie Sie sehen, gibt es überall die magische Zahl sieben plus minus zwei. Warum sollte Ihr Code also anders sein?

2 Antworten

Gute Frage – das Wort „Inversion“ ist etwas überraschend (da nach der Anwendung von DIP das Abhängigkeitsmodul auf niedrigerer Ebene jetzt offensichtlich nicht mehr vom Aufrufermodul auf höherer Ebene abhängt: Entweder der Aufrufer oder der Abhängige ist jetzt durch eine zusätzliche Abstraktion lockerer gekoppelt ).

Man könnte sich fragen, warum ich das Wort „Inversion“ verwende. Fairerweise muss man sagen, dass dies darauf zurückzuführen ist, dass traditionellere Softwareentwicklungsmethoden wie strukturierte Analyse und Design dazu neigen, Softwarestrukturen zu schaffen, in denen High-Level-Module von Low-Level-Modulen abhängen und in denen Abstraktionen von Details abhängen. Tatsächlich besteht einer der Zwecke dieser Methoden darin, eine Unterprogrammhierarchie zu definieren, die beschreibt, wie High-Level-Module Low-Level-Module aufrufen.... Somit ist die Abhängigkeitsstruktur eines gut gestalteten objektorientierten Programms „ „invertiert“ relativ zur Abhängigkeitsstruktur, die typischerweise das Ergebnis traditioneller Verfahrensmethoden ist.

Ein Punkt, den man beim Lesen von Onkel Bobs Aufsatz über DIP beachten sollte, ist, dass C++ keine Schnittstellen hat (und zum Zeitpunkt des Schreibens auch nicht hat), daher wird das Erreichen dieser Abstraktion in C++ normalerweise über eine abstrakte/rein virtuelle Basisklasse implementiert, während in Java oder C# ist die Abstraktion zum Lösen der Kopplung normalerweise die Entkopplung, indem die Schnittstelle von der Abhängigkeit abstrahiert und das/die Modul(e) der höheren Ebene an die Schnittstelle gekoppelt wird.

Bearbeiten Nur um klarzustellen:

„An manchen Stellen sehe ich es auch als Abhängigkeitsinversion“

Umkehrung: Umkehren des Abhängigkeitsmanagements von einer Anwendung zu einem Container (z. B. Spring).

Abhängigkeitsspritze:

Anstatt eine Factory-Vorlage zu schreiben, können Sie ein Objekt direkt in die Client-Klasse einfügen. Lassen Sie also die Client-Klasse auf die Schnittstelle verweisen und wir sollten in der Lage sein, einen konkreten Typ in die Client-Klasse einzufügen. Dadurch muss die Client-Klasse kein neues Schlüsselwort verwenden und ist vollständig von den konkreten Klassen getrennt.

Was ist mit der Inversion of Control (IoC)?

In der traditionellen Programmierung wird der Ablauf der Geschäftslogik durch statisch einander zugeordnete Objekte definiert. Bei der Umkehrung der Kontrolle hängt der Fluss von einem Objektgraphen ab, der von einer Assembler-Instanz erstellt und durch durch Abstraktionen definierte Objektinteraktionen ermöglicht wird. Der Bindungsprozess wird durch Abhängigkeitsinjektion erreicht, obwohl einige argumentieren, dass die Verwendung eines Service Locators auch eine Umkehrung der Kontrolle ermöglicht.

Die Umkehrung der Kontrolle als Designleitfaden dient folgenden Zwecken:

  • Es erfolgt eine Entkopplung der Ausführung einer bestimmten Aufgabe von ihrer Umsetzung.
  • Jedes Modul kann sich auf das konzentrieren, was es tun soll.
  • Module machen keine Annahmen darüber, was andere Systeme tun, sondern verlassen sich auf deren Verträge.
  • Der Austausch von Modulen hat keine Auswirkungen auf andere Module.

Weitere Informationen finden Sie unter.

Die Abhängigkeitsumkehr ist eine der wichtigsten Programmiersprachen. Es gibt überraschend wenige Beschreibungen dieses Idioms (Prinzips) im russischsprachigen Internet. Also beschloss ich, eine Beschreibung zu erstellen. Ich werde die Beispiele in Java machen; im Moment fällt es mir einfacher, obwohl das Prinzip der Abhängigkeitsinversion auf jede Programmiersprache anwendbar ist.

Diese Beschreibung wurde gemeinsam mit Vladimir Matveev zur Vorbereitung auf den Unterricht mit Java-Studenten entwickelt.

Weitere Artikel aus dieser Serie:

Lassen Sie mich mit der Definition von „Sucht“ beginnen. Was ist Sucht? Wenn Ihr Code intern eine Klasse verwendet oder explizit eine statische Methode einer Klasse oder Funktion aufruft, handelt es sich um eine Abhängigkeit. Lassen Sie es mich anhand von Beispielen erklären:

Unten erstellt Klasse A innerhalb einer Methode namens someMethod() explizit ein Objekt der Klasse B und ruft seine Methode someMethodOfB() auf.

Öffentliche Klasse A ( void someMethod() ( B b = new B(); b.someMethodOfB(); ) )

In ähnlicher Weise greift beispielsweise Klasse B explizit auf die statischen Felder und Methoden der Systemklasse zu:

Öffentliche Klasse B ( void someMethodOfB() ( System.out.println("Hello world"); ) )

In allen Fällen wird dies aufgerufen, wenn eine Klasse (Typ A) unabhängig eine Klasse (Typ B) erstellt oder explizit auf statische Felder oder Klassenmitglieder zugreift gerade Sucht. Diese. Wichtig: Wenn eine Klasse in sich selbst mit einer anderen Klasse zusammenarbeitet, handelt es sich um eine Abhängigkeit. Wenn es diese Klasse auch in sich selbst erstellt, dann dies gerade Sucht.

Was ist falsch an direkten Abhängigkeiten? Direkte Abhängigkeiten sind schlecht, weil eine Klasse, die unabhängig eine andere Klasse in sich selbst erstellt, „eng“ an diese Klasse gebunden ist. Diese. wenn explizit geschrieben ist, dass B = new B(); , dann funktioniert Klasse A immer mit Klasse B und keiner anderen Klasse. Oder wenn dort System.out.println("..."); steht. dann wird die Klasse immer an System.out und nirgendwo anders ausgegeben.

Für kleine Klassen sind Abhängigkeiten nicht schlimm. Dieser Code funktioniert möglicherweise recht gut. Damit Ihre Klasse A jedoch universell in der Umgebung verschiedener Klassen arbeiten kann, sind in einigen Fällen möglicherweise andere Implementierungen von Klassen erforderlich – Abhängigkeiten. Diese. Beispielsweise benötigen Sie nicht Klasse B, sondern eine andere Klasse mit derselben Schnittstelle oder nicht System.out, sondern beispielsweise eine Ausgabe an einen Logger (z. B. log4j).

Der direkte Zusammenhang lässt sich grafisch wie folgt darstellen:

Diese. wenn Sie Klasse A in Ihrem Code erstellen: A a = new A(); Tatsächlich wird nicht nur eine Klasse A erstellt, sondern eine ganze Hierarchie abhängiger Klassen, ein Beispiel dafür ist im Bild unten. Diese Hierarchie ist „starr“: Ohne den Quellcode einzelner Klassen zu ändern, können Sie keine der Klassen in der Hierarchie ersetzen. Daher ist Klasse A in einer solchen Implementierung schlecht an eine sich ändernde Umgebung anpassbar. Höchstwahrscheinlich kann es in keinem anderen Code als dem spezifischen Code verwendet werden, für den Sie es geschrieben haben.

Um Klasse A von bestimmten Abhängigkeiten zu entkoppeln, verwenden Sie Abhängigkeitsspritze. Was ist Abhängigkeitsinjektion? Anstatt die erforderliche Klasse explizit im Code zu erstellen, werden Abhängigkeiten über den Konstruktor an Klasse A übergeben:

Öffentliche Klasse A ( private final B b; public A(B b) ( this.b = b; ) public void someMethod() ( b.someMethodOfB(); ) )

Das. Klasse A erhält nun ihre Abhängigkeit durch den Konstruktor. Um nun Klasse A zu erstellen, müssen Sie zunächst ihre abhängige Klasse erstellen. In diesem Fall ist es B:

B b = neues B(); A a = neues A(b); a.someMethod();

Wenn der gleiche Vorgang für alle Klassen wiederholt wird, d. h. Übergeben Sie eine Instanz der Klasse D an den Konstruktor der Klasse B, ihre Abhängigkeiten E und F an den Konstruktor der Klasse D usw., dann erhalten Sie Code, in dem alle Abhängigkeiten in umgekehrter Reihenfolge erstellt werden:

G g = neues G(); H h = neues H(); F f = neu (g,h); E e = neues E(); D d = neues D(e,f); B b = neues B(d); A a = neues A(b); a.someMethod();

Grafisch lässt sich das so darstellen:

Wenn Sie zwei Bilder vergleichen – das Bild oben mit direkten Abhängigkeiten und das zweite Bild mit Abhängigkeitsinjektion – können Sie sehen, dass sich die Richtung der Pfeile in die entgegengesetzte Richtung geändert hat. Aus diesem Grund wird die Redewendung „Inversion“ von Abhängigkeiten genannt. Mit anderen Worten bedeutet Abhängigkeitsinversion, dass die Klasse keine Abhängigkeiten selbst erstellt, sondern diese in der erstellten Form im Konstruktor (oder auf andere Weise) erhält.

Warum ist die Abhängigkeitsumkehr gut? Mit der Abhängigkeitsumkehr können Sie alle Abhängigkeiten in einer Klasse ersetzen, ohne deren Code zu ändern. Dies bedeutet, dass Ihre Klasse A flexibel für die Verwendung in einem anderen Programm als dem, für das sie ursprünglich geschrieben wurde, konfiguriert werden kann. Das. Das Prinzip der Abhängigkeitsumkehr (manchmal auch als Prinzip der Abhängigkeitsinjektion bezeichnet) ist der Schlüssel zum Aufbau flexiblen, modularen und wiederverwendbaren Codes.

Der Nachteil der Abhängigkeitsinjektion ist auch auf den ersten Blick sichtbar: Objekte von Klassen, die nach diesem Muster entworfen wurden, sind arbeitsintensiv zu konstruieren. Daher wird die Abhängigkeitsinjektion normalerweise in Verbindung mit einer Bibliothek verwendet, die diese Aufgabe erleichtern soll. Zum Beispiel eine der Google Guice-Bibliotheken. Cm. .

Die Formulierung des Abhängigkeitsinversionsprinzips besteht aus zwei Regeln, deren Einhaltung sich äußerst positiv auf die Codestruktur auswirkt:

  • Module der obersten Ebene sollten nicht von Modulen der unteren Ebene abhängig sein. Beide müssen von einer Abstraktion abhängen.
  • Abstraktionen sollten nicht von Details abhängen. Details müssen von Abstraktionen abhängen.

Das klingt zunächst nicht sehr attraktiv, und der Leser ist wahrscheinlich schon auf einen sehr langweiligen Artikel mit einer Menge Begriffen, komplexen Redewendungen und Beispielen vorbereitet, von denen sowieso nichts klar ist. Aber vergebens, denn im Prinzip kommt es vorbehaltlich des Prinzips der Abhängigkeitsumkehr wieder auf die korrekte Verwendung und im Kontext der Hauptidee – der Wiederverwendung von Code – an.

Ein weiteres Konzept, das hier relevant sein wird, ist die schwache Kopplung von Typen, d. h. die Verringerung oder Beseitigung ihrer Abhängigkeit voneinander, was tatsächlich durch Abstraktion und Polymorphismus erreicht wird. Dies ist tatsächlich die Essenz des Prinzips der Abhängigkeitsumkehr.

Schauen wir uns nun ein Beispiel an, das deutlich zeigt, wie eine lose Kopplung in Aktion aussieht.

Nehmen wir an, wir beschließen, eine Geburtstagstorte zu bestellen. Dafür gingen wir zu einer wunderbaren Bäckerei an der Straßenecke. Wir fanden heraus, ob sie unter dem lauten Namen „Luxury“ einen Kuchen für uns backen könnten, und nachdem wir eine positive Antwort erhalten hatten, bestellten wir ihn. Es ist einfach.

Lassen Sie uns nun entscheiden, welche Abstraktionen in diesen Code aufgenommen werden müssen. Stellen Sie sich dazu einfach ein paar Fragen:

  • Warum Kuchen? Sie können auch einen Kuchen oder Cupcakes bestellen
  • Warum gerade zu deinem Geburtstag? Was wäre, wenn es eine Hochzeit oder ein Abschluss wäre?
  • Warum „Luxus“, was ist, wenn mir „Anthill“ oder „Prag“ besser gefällt?
  • Warum genau diese Bäckerei und nicht eine Konditorei in der Innenstadt oder woanders?

Und jedes dieser „Was wäre wenn“ und „Was wäre wenn“ sind Punkte, an denen eine Erweiterbarkeit des Codes und dementsprechend eine lose Kopplung und Definition von Typen durch Abstraktionen erforderlich ist.

Beginnen wir nun mit der Konstruktion der Typen selbst.

Wir definieren eine Schnittstelle für jedes Meisterwerk der Konditorei, das wir bestellen können.

Schnittstelle IPastry ( string Name ( get; set; ) )

Und hier ist eine konkrete Umsetzung für unseren Fall – eine Geburtstagstorte. Wie Sie sehen, gehören zu einer Geburtstagstorte traditionell Kerzen)))

Klasse BirthdayCake: IPastry ( public int NumberOfCandles ( get; set; ) public string Name ( get; set; ) public override string ToString() ( return String.Format("(0) with (1) Candle Nices", Name, NumberOfCandles ) ; ) )

Wenn wir nun eine Torte für eine Hochzeit oder einfach nur zum Tee benötigen oder Cupcakes oder Puddingkuchen möchten, haben wir für jeden eine einfache Benutzeroberfläche.

Die nächste Frage ist, wie sich Süßwaren voneinander unterscheiden (außer natürlich im Namen). Natürlich mit Rezept!

Und zum Konzept eines Rezepts gehören eine Zutatenliste und eine Beschreibung des Kochvorgangs. Daher werden wir eine separate Schnittstelle für das Zutatenkonzept haben:

Interface IIngredient ( string IngredientName ( get;set;) double Quantity ( get; set; ) string Units ( get; set; ) )

Und hier sind die Zutaten für unseren Kuchen: Mehl, Butter, Zucker und Sahne:

Klasse Flour:IIngredient ( public string IngredientName ( get; set; ) public double Quantity ( get; set; ) public string Units ( get; set; ) public string Quality ( get; set; ) ) class Butter: IIngredient ( public string IngredientName ( get; set; ) public double Quantity ( get; set; ) public string Units ( get; set; ) ) class Sugar: IIngredient ( public string IngredientName ( get; set; ) public double Quantity ( get; set; ) public string Units ( get; set; ) public string Kind ( get; set; ) ) class Creme: IIngredient ( public string IngredientName ( get; set; ) public double Quantity ( get; set; ) public string Units ( get; set; ) public double Fat ( get; set; ) )

Die Zutatenliste kann bei verschiedenen Rezepten und verschiedenen Süßwaren unterschiedlich sein, für unser Rezept reicht diese Liste jedoch aus.

Jetzt ist es an der Zeit, mit dem Konzept eines Rezepts fortzufahren. Was auch immer wir kochen, wir wissen auf jeden Fall, wie es heißt, was es ist, welche Zutaten in dem Gericht enthalten sind und wie man es zubereitet.

Schnittstelle IRecipe ( Typ PastryType ( get; set; ) string Name ( get; set;) IList Zutaten ( get;set;) string Beschreibung ( get;set;) )

Konkret sieht die Klasse, die ein Rezept für eine Geburtstagstorte darstellt, folgendermaßen aus:

Klasse BirthdayCakeRecipe: IRecipe ( public Type PastryType ( get; set; ) public string Name ( get; set;) public IList Ingredients ( get; set; ) public string Beschreibung ( get; set; ) public BirthdayCakeReipe() ( Ingredients = new List (); } }

Kommen wir nun zu unserer wunderbaren Bäckerei an der Straßenecke.

Natürlich könnten wir es auch auf viele andere Bäckereien anwenden, deshalb werden wir auch dafür eine grundlegende Schnittstelle definieren. Was ist für eine Bäckerei das Wichtigste? Fähigkeit, Produkte zu backen.

Schnittstelle IBakery ( IPastry Bake(IRecipe Rezept); )

Und hier ist die Klasse, die unsere Bäckerei repräsentiert:

Klasse NiceBakeryOnTheCornerOFMyStreet ( Dictionary Menü = neues Wörterbuch (); public void AddToMenu(IRecipe-Rezept) ( if (!menu.ContainsKey(recipe.Name)) ( menu.Add(recipe.Name, Rezept); ) else ( Console.WriteLine("Es ist bereits im Menü"); ) ) public IRecipe FindInMenu(string name) ( if (menu.ContainsKey(name)) ( return menu; ) Console.WriteLine("Sorry...momentan haben wir kein " + name); return null; ) public IPastry Bake (IRecipe-Rezept) ( if (recipe != null) ( IPastry pastry = Activator.CreateInstance(recipe.PastryType) as IPastry; if (pastry != null) ( pastry.Name = Recipe.Name; return pastry as IPastry; ) ) null zurückgeben; ) )

Jetzt müssen Sie nur noch den Code testen:

Klassenprogramm ( static void Main() ( //Erstellen einer Instanz der Bäckereiklasse var Bakery = new NiceBakeryOnTheCornerOFMyStreet(); //Vorbereiten von Zutaten für das Rezept var Mehl = new Flour() ( IngredientName = "Flour", Quantity = 1.5 , Units = "kg" ); var butter = new Butter() ( IngredientName = "Butter", Menge = 0,5, Units = "kg" ); var Sugar = new Sugar() ( IngredientName = "Sugar", Menge = 0,7 , Units = "kg" ); var creme = new Creme() ( IngredientName = "Creme", Quantity = 1.0, Units = "liters" ); //und das ist das Rezept selbst var weddingCakeRecipe = new BirthdayCakeRecipe() ( PastryType = typeof(BirthdayCake), Name = „Birthday Cake Luxury“, Description = „Beschreibung, wie man eine schöne Geburtstagstorte backt“ ); WeddingCakeRecipe.Ingredients.Add(Mehl); WeddingCakeRecipe.Ingredients.Add(Butter); WeddingCakeRecipe.Ingredients .Add(sugar); weddingCakeRecipe.Ingredients.Add(creme); //Hinzufügen unseres Kuchenrezepts zur Speisekarte der Bäckerei breadry.AddToMenu(weddingCakeRecipe); //Jetzt bestellen wir es!! BirthdayCake cake = Bakery.Bake(bakery.FindInMenu("Birthday Cake Luxury")) as BirthdayCake; //einige Kerzen hinzufügen ;) cake.NumberOfCandles = 10; //und hier sind wir !!! Console.WriteLine(cake); ) )

Schauen wir uns nun noch einmal den gesamten Code an und werten ihn aus. Der Code ist recht einfach, die Typen, ihre Abstraktionen, Daten und Funktionalität sind klar abgegrenzt, der Code ermöglicht Erweiterung und Wiederverwendung. Jedes Segment kann problemlos durch ein anderes ersetzt werden, das dem Basistyp entspricht, und der Rest des Codes wird dadurch nicht zum Absturz gebracht.

Sie können endlos Arten von Zutaten und Rezepte für verschiedene Arten von Süßwarenprodukten hinzufügen und andere Klassen erstellen, die Bäckereien, Konditoreien und andere ähnliche Einrichtungen beschreiben.

Kein schlechtes Ergebnis. Und das alles dank der Tatsache, dass wir versucht haben, die Klassen auf ein Minimum zu beschränken.

Denken wir nun über die Folgen einer Verletzung des Prinzips der Abhängigkeitsumkehr nach:

  1. Starrheit (es wäre sehr schwierig, Änderungen am System vorzunehmen, da jede Änderung viele verschiedene Teile davon betraf).
  2. Fragilität (wenn Änderungen an einem Teil des Systems vorgenommen werden, werden andere Teile davon angreifbar, und manchmal ist dies auf den ersten Blick nicht allzu offensichtlich).
  3. Unbeweglichkeit (Sie können die Wiederverwendung von Code in anderen Systemen vergessen, da die Module stark miteinander verbunden sind).

Ziehen Sie nun Ihre eigenen Schlussfolgerungen darüber, wie nützlich das Prinzip der Abhängigkeitsinversion im Code ist. Ich denke, die Antwort liegt auf der Hand.

© 2023 youmebox.ru – Über das Geschäft – Portal mit nützlichem Wissen