Warum ist es für „niedrigere“ Anwendungsebenen eine gute Idee, sich der „höheren“ nicht bewusst zu sein?


66

In einer typischen (gut gestalteten) MVC-Webanwendung kennt die Datenbank den Modellcode nicht, der Modellcode kennt den Controller-Code nicht und der Controller-Code kennt den Ansichtscode nicht. (Ich stelle mir vor, Sie könnten sogar so weit wie die Hardware oder sogar noch weiter unten beginnen, und das Muster könnte dasselbe sein.)

Wenn Sie in die andere Richtung gehen, können Sie nur eine Schicht nach unten gehen. Die Ansicht kann den Controller kennen, aber nicht das Modell. Der Controller kann das Modell kennen, aber nicht die Datenbank. Das Modell kann die Datenbank kennen, aber nicht das Betriebssystem. (Alles, was tiefer geht, ist wahrscheinlich irrelevant.)

Ich kann intuitiv verstehen, warum dies eine gute Idee ist, aber ich kann es nicht artikulieren. Warum ist diese unidirektionale Schichtung eine gute Idee?


10
Vielleicht liegt es daran, dass die Daten aus der Datenbank in die Ansicht "hoch" kommen. Es "startet" in der Datenbank und "kommt" in der Ansicht an. Das Ebenenbewusstsein geht in die entgegengesetzte Richtung, wenn die Daten "wandern". Ich verwende gerne "Anführungszeichen".
Jason Swett

1
Sie haben es in Ihrem letzten Satz markiert: Unidirektional. Warum sind verknüpfte Listen weitaus typischer als doppelt verknüpfte Listen? Die Pflege von Beziehungen wird mit einer einfach verknüpften Liste unendlich einfacher. Auf diese Weise erstellen wir Abhängigkeitsgraphen, da rekursive Aufrufe weniger wahrscheinlich sind und die allgemeinen Diagrammeigenschaften insgesamt einfacher zu beurteilen sind. Angemessene Strukturen sind von Natur aus besser wartbar, und dieselben Dinge, die sich auf Grafiken auf Mikroebene (Implementierung) auswirken, auch auf Makroebene (Architektur).
Jimmy Hoffa

2
Ich glaube nicht, dass es in den meisten Fällen eine gute Praxis ist, wenn der View den Controller kennt. Da Controller die Ansicht fast immer kennen, wird durch die Kenntnisnahme der Ansicht über den Controller ein Zirkelverweis erstellt
Amy Blankenship,

8
Schlechte Analogiezeit: Aus dem gleichen Grund ist im Allgemeinen derjenige für den Unfall verantwortlich und verantwortlich, der Sie von hinten mit einem Auto rammt, während Sie fahren. Er kann sehen, was Sie vorhaben und sollte die Kontrolle haben, und wenn er Sie nicht vermeiden kann, bedeutet dies, dass er die Sicherheitsregeln nicht beachtet. Nicht umgekehrt. Und durch die Verkettung macht er sich keine Sorgen mehr darüber, was hinter ihm vor sich geht.
Haylem

1
Offensichtlich kennt eine Ansicht ein stark typisiertes Ansichtsmodell.
DazManCat

Antworten:


121

Schichten, Module, ja die Architektur selbst, dienen dazu , Computerprogramme für den Menschen verständlicher zu machen . Die numerisch optimale Methode zur Lösung eines Problems ist fast immer ein Wirrwarr von nicht-modularem, selbstreferenzierendem oder sogar selbstmodifizierendem Code - egal, ob es sich um stark optimierten Assembler-Code in eingebetteten Systemen handelt, die nach Millionen von Jahren Speicherbeschränkungen oder DNA-Sequenzen verkrüppeln des Auswahldrucks. Solche Systeme haben keine Schichten, keine erkennbare Richtung des Informationsflusses, tatsächlich keine Struktur, die wir überhaupt erkennen können. Für alle, außer für ihren Autor, scheinen sie mit purer Magie zu arbeiten.

In der Softwareentwicklung wollen wir das vermeiden. Gute Architektur ist eine bewusste Entscheidung, auf Effizienz zu verzichten, um das System für normale Menschen verständlich zu machen. Es ist einfacher, jeweils eine Sache zu verstehen, als zwei Dinge, die nur zusammen Sinn ergeben. Deshalb sind Module und Layer eine gute Idee.

Es ist jedoch unumgänglich, dass Module Funktionen voneinander aufrufen und Layer übereinander erstellt werden. In der Praxis ist es also immer notwendig, Systeme so zu konstruieren, dass einige Teile andere Teile erfordern. Der bevorzugte Kompromiss besteht darin, sie so zu bauen, dass ein Teil einen anderen benötigt, aber dieser Teil nicht den ersten zurück benötigt. Und genau das gibt uns die unidirektionale Schichtung: Es ist möglich, das Datenbankschema zu verstehen, ohne die Geschäftsregeln zu kennen, und die Geschäftsregeln zu verstehen, ohne die Benutzeroberfläche zu kennen. Es wäre schön, in beide Richtungen unabhängig zu sein - jemandem die Möglichkeit zu geben, eine neue Benutzeroberfläche zu programmieren, ohne etwas zu wissenüberhaupt über die Geschäftsregeln - aber in der Praxis ist dies so gut wie nie möglich. Faustregeln wie "Keine zyklischen Abhängigkeiten" oder "Abhängigkeiten dürfen nur eine Ebene tiefer gehen" erfassen einfach die praktisch erreichbare Grenze der Grundidee, dass jeweils eine Sache leichter zu verstehen ist als zwei Dinge.


1
Was meinen Sie mit "das System für normale Menschen verständlich machen "? Ich denke, wenn man es so formuliert, ermutigt es neue Programmierer, Ihre guten Punkte abzulehnen, weil sie, wie die meisten Leute, denken, sie seien schlauer als die meisten Leute, und dies wird für sie kein Problem sein. Ich würde sagen "Das System für den Menschen verständlich machen"
Thomas Bonini

12
Diese Lektüre ist für diejenigen erforderlich, die eine vollständige Entkopplung für das Ideal halten, das sie anstreben sollten, aber nicht verstehen können, warum dies nicht funktioniert.
Robert Harvey

6
Nun, @ Andreas, es gibt immer Mel .
TRiG

6
Ich denke, "leichter zu verstehen" ist nicht genug. Es geht auch darum, das Ändern, Erweitern und Verwalten des Codes zu vereinfachen.
Mike Weller

1
@Peri: Ein solches Gesetz existiert, siehe en.wikipedia.org/wiki/Law_of_Demeter . Ob Sie damit einverstanden sind oder nicht, ist eine andere Sache.
Mike Chamberlain

61

Die grundlegende Motivation ist folgende: Sie möchten in der Lage sein, eine ganze Ebene herauszureißen und eine völlig andere (umgeschriebene) zu ersetzen, und NIEMAND SOLLTE (KANN) DEN UNTERSCHIED ERKENNEN.

Das offensichtlichste Beispiel ist das Herausreißen der untersten Schicht und das Ersetzen einer anderen. Dies tun Sie, wenn Sie die obere (n) Schicht (en) gegen eine Simulation der Hardware entwickeln und dann die reale Hardware ersetzen.

Das nächste Beispiel ist das Herausreißen einer mittleren Ebene und Ersetzen einer anderen mittleren Ebene. Stellen Sie sich eine Anwendung vor, die ein Protokoll verwendet, das über RS-232 ausgeführt wird. Eines Tages muss man die Kodierung des Protokolls komplett ändern, weil sich "etwas anderes geändert hat". (Beispiel: Umschalten von direkter ASCII-Codierung auf Reed-Solomon-Codierung von ASCII-Streams, da Sie über eine Funkverbindung von Downtown LA nach Marina Del Rey und jetzt über eine Funkverbindung von Downtown LA zu einer Sonde arbeiten, die Europa umkreist , einer der Monde des Jupiter, und dieser Link benötigt eine viel bessere Vorwärtsfehlerkorrektur.)

Die einzige Möglichkeit, dies zu erreichen, besteht darin, dass jede Ebene eine bekannte, definierte Schnittstelle in die darüber liegende Ebene exportiert und eine bekannte, definierte Schnittstelle für die darunter liegende Ebene erwartet.

Nun ist es nicht genau der Fall, dass untere Schichten NICHTS über obere Schichten wissen. Vielmehr weiß die untere Schicht, dass die unmittelbar darüber liegende Schicht genau gemäß ihrer definierten Schnittstelle arbeitet. Es kann nichts mehr wissen, da per Definition alles, was sich nicht in der definierten Schnittstelle befindet, OHNE HINWEIS geändert werden kann.

Die RS-232-Schicht weiß nicht, ob ASCII, Reed-Solomon, Unicode (arabische Codepage, japanische Codepage, Rigellian Beta-Codepage) oder was ausgeführt wird. Es weiß nur, dass es eine Folge von Bytes erhält und diese Bytes in einen Port schreibt. Nächste Woche könnte er eine ganz andere Folge von Bytes von etwas ganz anderem bekommen. Es ist ihm egal. Er verschiebt nur Bytes.

Die erste (und beste) Erklärung des Schichtdesigns ist Dijkstra's klassisches Papier "Structure of the THE Multiprogramming System" . Es ist erforderlich, in diesem Geschäft zu lesen.


Dies ist hilfreich und vielen Dank für den Link. Ich wünschte, ich könnte zwei Antworten als die beste auswählen. Ich habe im Grunde genommen eine Münze in meinem Kopf geworfen und die andere ausgewählt, aber ich habe immer noch deine positiv bewertet.
Jason Swett

+1 für hervorragende Beispiele. Mir gefällt die Erklärung von JRS
ViSu

@JasonSwett: Wenn ich die Münze geworfen hätte, hätte ich sie geworfen, bis sie diese Antwort kennzeichnet! ^^ +1 an John.
Olivier Dulac

Ich bin damit nicht einverstanden, da Sie selten in der Lage sein möchten, die Geschäftsregelebene herauszureißen und mit einer anderen auszutauschen. Geschäftsregeln ändern sich viel langsamer als Benutzeroberflächen- oder Datenzugriffstechnologien.
Andy

Ding Ding Ding!!! Ich denke, das Wort, nach dem Sie gesucht haben, ist "Entkopplung". Dafür sind gute APIs da. Definieren der öffentlichen Schnittstellen eines Moduls, damit es universell verwendet werden kann.
Evan Plaice

8

Denn die höheren Ebenen können sich ändern.

In diesem Fall sollte eine modulare (dh unidirektionale) Anwendung aufgrund geänderter Anforderungen, neuer Benutzer, unterschiedlicher Technologien weniger Wartung erfordern und einfacher an die neuen Anforderungen angepasst werden können.


4

Ich denke, der Hauptgrund dafür ist, dass die Dinge enger miteinander verbunden werden. Je enger die Kopplung, desto eher treten später Probleme auf. Siehe diesen Artikel mehr Info: Kupplung

Hier ist ein Auszug:

Nachteile

Eng gekoppelte Systeme neigen dazu, die folgenden Entwicklungseigenschaften aufzuweisen, die häufig als Nachteile angesehen werden: Eine Änderung in einem Modul erzwingt normalerweise eine Welligkeit der Änderungen in anderen Modulen. Der Zusammenbau von Modulen kann aufgrund der erhöhten Abhängigkeit zwischen Modulen mehr Aufwand und / oder Zeit erfordern. Ein bestimmtes Modul ist möglicherweise schwerer wiederzuverwenden und / oder zu testen, da abhängige Module enthalten sein müssen.

Daher ist es aus Gründen der Leistung sinnvoll, ein enger gekoppeltes System zu haben. Der Artikel, den ich erwähnte, enthält auch einige Informationen dazu.


4

IMO, es ist sehr einfach. Sie können etwas nicht wiederverwenden, das ständig auf den Kontext verweist, in dem es verwendet wird.


4

Ebenen sollten keine wechselseitigen Abhängigkeiten haben

Die Vorteile einer Layer-Architektur bestehen darin, dass die Layer unabhängig voneinander verwendet werden können:

  • Sie sollten in der Lage sein, eine andere Präsentationsebene zusätzlich zur ersten zu erstellen, ohne die untere Ebene zu ändern (z. B. eine API-Ebene zusätzlich zu einer vorhandenen Weboberfläche erstellen).
  • Sie sollten in der Lage sein, die untere Schicht umzugestalten oder zu ersetzen, ohne die obere Schicht zu ändern

Diese Bedingungen sind grundsätzlich symmetrisch . Sie erklären, warum es im Allgemeinen besser ist, nur eine Abhängigkeitsrichtung zu haben, aber nicht welche .

Die Abhängigkeitsrichtung sollte der Befehlsrichtung folgen

Der Grund, warum wir eine Abhängigkeitsstruktur von oben nach unten bevorzugen, ist, dass die oberen Objekte die unteren Objekte erstellen und verwenden . Eine Abhängigkeit ist im Grunde eine Beziehung, die bedeutet, dass "A von B abhängt, wenn A ohne B nicht funktionieren kann ". Wenn also die Objekte in A die Objekte in B verwenden, sollten Abhängigkeiten so verlaufen.

Dies ist in gewisser Weise willkürlich. In anderen Mustern, wie z. B. MVVM, erfolgt die Steuerung problemlos über die unteren Ebenen. Sie können beispielsweise eine Beschriftung einrichten, deren sichtbare Beschriftung an eine Variable gebunden ist und sich mit dieser ändert. Normalerweise ist es immer noch vorzuziehen, Top-Down-Abhängigkeiten zu haben, da die Hauptobjekte immer diejenigen sind, mit denen der Benutzer interagiert, und diese Objekte den größten Teil der Arbeit erledigen.

Während wir von oben nach unten Methodenaufrufe verwenden, verwenden wir (normalerweise) von unten nach oben Ereignisse. Mit Ereignissen können Abhängigkeiten auch dann von oben nach unten verschoben werden, wenn das Steuerelement umgekehrt ausgeführt wird. Die Objekte der obersten Ebene abonnieren Ereignisse auf der untersten Ebene. Die unterste Ebene weiß nichts über die oberste Ebene, die als Plug-In fungiert.

Es gibt auch andere Möglichkeiten, eine einzelne Richtung beizubehalten, zum Beispiel:

  • Fortsetzungen (Übergabe eines Lambda oder einer aufzurufenden Methode und eines Ereignisses an eine asynchrone Methode)
  • Unterklasse (erstelle eine Unterklasse in A einer übergeordneten Klasse in B, die dann in die unterste Ebene eingefügt wird, ein bisschen wie ein Plugin)

3

Ich möchte meine zwei Cent zu dem hinzufügen, was Matt Fenwick und Kilian Foth bereits erklärt haben.

Ein Prinzip der Softwarearchitektur besteht darin, dass komplexe Programme durch Zusammensetzen kleinerer, in sich geschlossener Blöcke (Black Boxes) erstellt werden sollten: Dies minimiert Abhängigkeiten und reduziert somit die Komplexität. Daher ist diese unidirektionale Abhängigkeit eine gute Idee, da sie das Verständnis der Software erleichtert und die Verwaltung der Komplexität eines der wichtigsten Probleme bei der Softwareentwicklung ist.

In einer Schichtenarchitektur sind die unteren Schichten also Black Boxes, die Abstraktionsschichten implementieren, auf denen die oberen Schichten aufgebaut sind. Wenn eine untere Ebene (z. B. Ebene B) Details einer oberen Ebene A sehen kann, ist B keine Black Box mehr: Die Implementierungsdetails hängen von einigen Details des eigenen Benutzers ab, aber die Idee einer Black Box ist, dass dies der Fall ist Inhalt (seine Implementierung) ist für seinen Benutzer irrelevant!


3

Nur zum Spaß.

Denken Sie an eine Pyramide von Cheerleadern. Die unterste Reihe stützt die darüber liegenden Reihen.

Wenn die Cheerleaderin in dieser Reihe nach unten schaut, sind sie stabil und bleiben im Gleichgewicht, damit die über ihr nicht fallen.

Wenn sie aufschaut, um zu sehen, wie es allen über ihr geht, verliert sie das Gleichgewicht und der gesamte Stapel fällt herunter.

Nicht wirklich technisch, aber es war eine Analogie, von der ich dachte, dass sie helfen könnte.


3

Das einfache Verständnis und zu einem gewissen Grad austauschbare Komponenten sind sicherlich gute Gründe. Ein ebenso wichtiger Grund (und wahrscheinlich der Grund, warum Schichten überhaupt erfunden wurden) ist aus der Sicht der Softwarewartung. Die Quintessenz ist, dass Abhängigkeiten dazu führen können, dass Dinge kaputt gehen.

Angenommen, A hängt von B ab. Da nichts von A abhängt, können Entwickler A nach Herzenslust ändern, ohne befürchten zu müssen, dass sie etwas anderes als A brechen könnten. Wenn der Entwickler jedoch B ändern möchte, ist jede Änderung möglich Dies war ein häufiges Problem in den frühen Computertagen (denken Sie an strukturierte Entwicklung), in denen Entwickler einen Fehler in einem Teil des Programms beheben und Fehler in scheinbar völlig unabhängigen Teilen des Programms an anderer Stelle auslösen. Alles wegen Abhängigkeiten.

Um mit dem Beispiel fortzufahren, sei nun angenommen, dass A von B und B von A abhängt. IOW, eine zirkuläre Abhängigkeit. Wenn eine Änderung an einer beliebigen Stelle vorgenommen wird, kann das andere Modul möglicherweise beschädigt werden. Eine Änderung in B könnte immer noch A brechen, aber jetzt könnte eine Änderung in A auch B brechen.

Wenn Sie also in einem kleinen Team für ein kleines Projekt arbeiten, ist das alles ziemlich übertrieben, da Sie die Module nach Belieben wechseln können. Wenn Sie sich jedoch in einem umfangreichen Projekt befinden und alle Module von den anderen abhängig sind, können die anderen Module bei jeder erforderlichen Änderung möglicherweise beschädigt werden. Bei einem großen Projekt kann es schwierig sein, alle Auswirkungen zu ermitteln, sodass Sie wahrscheinlich einige Auswirkungen verpassen werden.

Bei einem großen Projekt mit vielen Entwicklern (z. B. einige, die nur Schicht A, einige Schicht B und einige Schicht C bearbeiten) wird es noch schlimmer. Da es wahrscheinlich wird, dass jede Änderung mit Mitgliedern auf den anderen Ebenen besprochen / diskutiert werden muss, um sicherzustellen, dass Ihre Änderungen nicht brechen oder eine Überarbeitung erzwingen, an der sie gerade arbeiten. Wenn Ihre Änderungen Änderungen für andere erzwingen, müssen Sie sie davon überzeugen, dass sie die Änderung vornehmen sollen, da sie nicht mehr Arbeit übernehmen möchten, nur weil Sie diese großartige neue Art der Ausführung von Dingen in Ihrem Modul haben. IOW, ein bürokratischer Albtraum.

Wenn Sie jedoch die Abhängigkeiten von A auf B beschränken, hängt B von C ab, und nur Mitarbeiter der Ebene C müssen ihre Änderungen für beide Teams koordinieren. Ebene B muss nur die Änderungen mit dem Team von Ebene A koordinieren, und das Team von Ebene A kann tun, was immer es will, da sich der Code nicht auf Ebene B oder C auswirkt. Im Idealfall gestalten Sie Ihre Ebenen so, dass sich Ebene C stark ändert Wenig, Schicht B verändert sich etwas und Schicht A macht den größten Teil der Veränderung.


+1 Bei meinem Arbeitgeber haben wir tatsächlich ein internes Diagramm, das den Kern Ihres letzten Absatzes beschreibt, der für das Produkt gilt, an dem ich arbeite, dh je weiter Sie Ihren Stack senken, desto geringer ist die Änderungsrate (und sollte es sein).
RobV

1

Der grundlegendste Grund, warum die unteren Schichten sich der höheren Schichten nicht bewusst sein sollten, ist, dass es viel mehr Arten höherer Schichten gibt. Auf Ihrem Linux-System befinden sich beispielsweise Tausende und Abertausende verschiedener Programme, die jedoch dieselbe C-Bibliotheksfunktion aufrufen malloc. Die Abhängigkeit besteht also von diesen Programmen zu dieser Bibliothek.

Beachten Sie, dass "untere Schichten" eigentlich die mittleren Schichten sind.

Stellen Sie sich eine Anwendung vor, die über einige Gerätetreiber über die Außenwelt kommuniziert. Das Betriebssystem ist in der Mitte .

Das Betriebssystem ist weder von Details in den Anwendungen noch von den Gerätetreibern abhängig. Es gibt viele Arten von Gerätetreibern desselben Typs, die dasselbe Gerätetreiber-Framework verwenden. Manchmal müssen Kernel-Hacker wegen einer bestimmten Hardware oder eines bestimmten Geräts eine spezielle Fallbehandlung in das Framework integrieren (aktuelles Beispiel: PL2303-spezifischer Code im USB-Serial-Framework von Linux). Wenn das passiert, schreiben sie normalerweise Kommentare darüber, wie viel das saugt und entfernt werden sollte. Obwohl das Betriebssystem Funktionen in den Treibern aufruft, werden die Aufrufe über Hooks ausgeführt, die dafür sorgen, dass die Treiber gleich aussehen, während Treiber beim Aufrufen des Betriebssystems häufig bestimmte Funktionen direkt über den Namen verwenden.

In gewisser Weise ist das Betriebssystem also aus Sicht der Anwendung und aus Sicht der Anwendung eine untergeordnete Ebene : eine Art Kommunikationsknotenpunkt, an dem Verbindungen hergestellt und Daten auf die entsprechenden Pfade umgeschaltet werden. Es hilft beim Entwurf des Kommunikations-Hubs, einen flexiblen Dienst zu exportieren, der von allen Benutzern verwendet werden kann, und keine geräte- oder anwendungsspezifischen Hacks in den Hub zu verschieben.


Ich bin glücklich, solange ich mir keine Sorgen um die Einstellung bestimmter Spannungen an bestimmten CPU-Pins machen muss :)
ein Lebenslauf

1

Die Trennung von Anliegen und das Teilen / Erobern von Ansätzen kann eine weitere Erklärung für diese Frage sein. Die Trennung von Bedenken bietet die Möglichkeit der Portabilität und in einigen komplexeren Architekturen bietet sie der Plattform unabhängige Skalierungs- und Leistungsvorteile.

Wenn Sie in diesem Zusammenhang an eine fünfstufige Architektur denken (Client, Präsentation, Geschäft, Integration und Ressourcenebene), sollte die niedrigere Ebene der Architektur die Logik und das Geschäft der höheren Ebenen nicht berücksichtigen und umgekehrt. Ich meine mit niedrigerer Ebene als Integrations- und Ressourcenebene. In der Integration bereitgestellte Schnittstellen zur Datenbankintegration sowie echte Datenbank- und Webservices (Datenanbieter von Drittanbietern) gehören zur Ressourcenschicht. Nehmen wir also an, Sie ändern Ihre MySQL-Datenbank in eine NoSQL-Dokument-Datenbank wie MangoDB, was die Skalierbarkeit oder was auch immer angeht.

Bei diesem Ansatz ist es der Geschäftsschicht egal, wie die Integrationsschicht die Verbindung / Übertragung durch die Ressource bereitstellt. Es wird nur nach Datenzugriffsobjekten gesucht, die von der Integrationsebene bereitgestellt werden. Dies könnte auf weitere Szenarien ausgeweitet werden. Grundsätzlich könnte die Trennung von Bedenken der Hauptgrund dafür sein.


1

In Anlehnung an Kilian Foths Antwort entspricht diese Schichtungsrichtung einer Richtung, in der ein Mensch ein System erforscht.

Stellen Sie sich vor, Sie sind ein neuer Entwickler, der einen Fehler im geschichteten System beheben soll.

Bugs sind normalerweise ein Missverhältnis zwischen dem, was der Kunde braucht und dem, was er bekommt. Da der Kunde über die Benutzeroberfläche mit dem System kommuniziert und über die Benutzeroberfläche ein Ergebnis erhält (UI bedeutet wörtlich „Benutzeroberfläche“), werden die Fehler auch in Bezug auf die Benutzeroberfläche gemeldet. Als Entwickler haben Sie also keine andere Wahl, als sich auch die Benutzeroberfläche anzuschauen, um herauszufinden, was passiert ist.

Aus diesem Grund sind Verbindungen von oben nach unten erforderlich. Warum haben wir keine Verbindungen in beide Richtungen?

Nun, Sie haben drei Szenarien, in denen dieser Fehler jemals auftreten könnte.

Es könnte im UI-Code selbst vorkommen und dort lokalisiert werden. Dies ist einfach, Sie müssen nur einen Platz finden und ihn reparieren.

Dies kann in anderen Teilen des Systems aufgrund von Aufrufen über die Benutzeroberfläche auftreten. Was mäßig schwierig ist, Sie verfolgen einen Baum von Anrufen, finden einen Ort, an dem der Fehler auftritt, und beheben ihn.

Und es könnte als Ergebnis eines Aufrufs Ihres UI-Codes auftreten. Was schwierig ist, Sie müssen den Anruf abfangen, seine Quelle finden und dann herausfinden, wo der Fehler auftritt. Angenommen, ein Punkt, an dem Sie beginnen, befindet sich tief unten in einem einzelnen Zweig eines Anrufbaums, UND Sie müssen erst einen korrekten Anrufbaum finden. Möglicherweise gibt es mehrere Aufrufe im UI-Code. Das Debuggen ist für Sie ausgeschnitten.

Um den schwierigsten Fall so weit wie möglich zu beseitigen, wird von den kreisförmigen Abhängigkeiten dringend abgeraten. Die Schichten werden meist von oben nach unten verbunden. Auch wenn eine Verbindung in die andere Richtung benötigt wird, ist sie in der Regel begrenzt und klar definiert. Selbst bei Rückrufen, bei denen es sich um eine Art umgekehrte Verbindung handelt, stellt der Code, der beim Rückruf aufgerufen wird, diesen Rückruf in der Regel an erster Stelle bereit, indem eine Art "Opt-In" für umgekehrte Verbindungen implementiert und deren Auswirkungen auf das Verständnis von a begrenzt werden System.

Layering ist ein Tool und richtet sich in erster Linie an Entwickler, die ein vorhandenes System unterstützen. Nun, Verbindungen zwischen Schichten spiegeln auch dies wider.


-1

Ein weiterer Grund, den ich hier ausdrücklich erwähnen möchte, ist die Wiederverwendbarkeit von Code . Wir haben bereits das Beispiel des RS232-Mediums gehabt, das ersetzt wird. Gehen wir also noch einen Schritt weiter ...

Stellen Sie sich vor, Sie entwickeln Treiber. Es ist deine Aufgabe und du schreibst eine Menge. Protokolle werden wahrscheinlich irgendwann wiederholt, ebenso wie physische Medien.

Sie werden also anfangen, wiederverwendbare Ebenen für diese Dinge zu schreiben, es sei denn, Sie sind ein großer Fan davon, immer und immer wieder dasselbe zu tun.

Angenommen, Sie müssen 5 Treiber für Modbus-Geräte schreiben. Einer von ihnen verwendet Modbus TCP, zwei verwenden Modbus auf RS485 und der Rest geht über RS232. Sie werden Modbus nicht fünfmal neu implementieren, da Sie fünf Treiber schreiben. Außerdem werden Sie Modbus nicht dreimal neu implementieren, da sich unter Ihnen drei verschiedene physikalische Ebenen befinden.

Sie schreiben einen TCP-Medienzugriff, einen RS485-Medienzugriff und möglicherweise einen RS232-Medienzugriff. Ist es klug zu wissen, dass es zu diesem Zeitpunkt eine Modbus-Schicht darüber geben wird? Wahrscheinlich nicht. Der nächste Treiber, den Sie implementieren, verwendet möglicherweise auch Ethernet, jedoch HTTP-REST. Es wäre eine Schande, wenn Sie den Ethernet Media Access neu implementieren müssten, um über HTTP zu kommunizieren.

Eine Schicht darüber, Sie werden Modbus nur einmal implementieren. Diese Modbus-Schicht wird wiederum nicht die Treiber kennen, die eine Schicht höher sind. Diese Treiber müssen natürlich wissen, dass sie Modbus sprechen sollen, und sie müssen wissen, dass sie Ethernet verwenden. Unabhängig davon, wie ich es beschrieben habe, konnte man nicht nur eine Ebene herausreißen und ersetzen. Sie könnten natürlich - und das ist für mich der größte Vorteil von allen - diese vorhandene Ethernet-Schicht für etwas verwenden, das absolut nichts mit dem Projekt zu tun hat, das ursprünglich für die Erstellung verantwortlich war.

Das ist etwas, was wir wahrscheinlich jeden Tag als Entwickler sehen und das spart uns jede Menge Zeit. Es gibt unzählige Bibliotheken für alle Arten von Protokollen und anderen Dingen. Diese existieren aufgrund von Prinzipien wie der Abhängigkeitsrichtung, die der Befehlsrichtung folgt, wodurch wir wiederverwendbare Softwareschichten erstellen können.


Wiederverwertbarkeit bereits in einen wurde explizit erwähnt Antwort geschrieben mehr als halbes Jahr her
gnat
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.