Read in English · Leer en español · Lire en français
Abschnitt 0.3 von 0.8 · Einsteiger · 16 Min. Lesezeit
Funktionen, Module, Objekte, Schnittstellen, Bibliotheken und Frameworks
Videolektion
5 Min. · auf Englisch gesprochen, mit UntertitelnNur anhören (Audio)
Transkript lesen
Denken Sie an ein großes Restaurant. Statt eines Kochs, der alles macht, gibt es Stationen: Grill, Patisserie und Salate, jede mit eigenem Personal, Werkzeug und Rezepten. Bestellungen wandern auf Bons zwischen den Stationen, sodass der Grill nicht wissen muss, wie der Patissier arbeitet. Code ist ganz genauso organisiert, und diese Lektion zeigt, wie.
Der kleinste Baustein ist die Funktion: ein benanntes Stück Code, das eine Aufgabe erledigt. Denken Sie an einen Kaffeevollautomaten. Sie geben Bohnen, Wasser und eine Getränkewahl hinein, und heraus kommt ein Kaffee. Mit Funktionen schreiben Sie eine Regel einmal und verwenden sie wieder. Ein Shop könnte seine Mehrwertsteuerberechnung in eine Funktion schreiben und sie vom Checkout, von der Rechnung und von der Erstattungsmaske aufrufen. Ändert sich dann der Steuersatz, ändert sich eine Funktion, nicht zwanzig verstreute Kopien, die bald auseinanderlaufen würden.
Als Nächstes werden zusammengehörige Funktionen zu Modulen gruppiert und zusammengehörige Module zu einem Paket. Hier enthält ein Paket namens shop zwei Module: pricing und orders. Jedes Modul hat einen kleinen öffentlichen Teil, den andere Module nutzen sollen, und private Details. Im Modul pricing ist die Mehrwertsteuerfunktion öffentlich. Die Hilfsfunktion darunter ist privat, in Python gekennzeichnet durch einen führenden Unterstrich. Das Modul orders ruft nur die öffentliche Preisfunktion auf, nie die private Hilfsfunktion. Denken Sie an eine Abteilung mit einem öffentlichen Empfang und internen Abläufen, die niemand außerhalb sehen muss. Der Wert dieser Trennung: Der private Teil lässt sich frei neu schreiben, ohne bei anderen etwas kaputt zu machen, solange der öffentliche Teil seine Versprechen hält.
Viele Sprachen erlauben es, Daten mit den Funktionen zu bündeln, die mit ihnen arbeiten. Eine Klasse ist die Vorlage, und ein Objekt ist ein einzelnes Exemplar daraus. Eine Bankkonto-Klasse legt fest, dass jedes Konto einen Inhaber und einen Kontostand hat; das Konto jedes Kunden ist ein Objekt. Die zentrale Idee ist die Kapselung. Könnte jeder Code den Kontostand direkt setzen, würde nichts verhindern, dass er negativ wird. Stattdessen ist die Methode withdraw die einzige Tür, und sie prüft jedes Mal die Regeln, wie ein Bankschalter vor dem Tresorraum.
Die wichtigste Idee dieser Lektion ist die Schnittstelle. Sie ist ein Vertrag: die Operationen, die ein Teil anderen anbietet, was sie erwarten, was sie zurückgeben und was schiefgehen kann. Eine Steckdose ist eine Schnittstelle: Jedes Gerät mit dem richtigen Stecker funktioniert. Darum ist das wichtig. Angenommen, der Checkout eines Shops ruft überall direkt die eigenen Funktionen eines Zahlungsanbieters auf. Wechselt der Shop im nächsten Jahr den Anbieter, muss der Checkout neu geschrieben werden. Stattdessen definiert der Shop seine eigene kleine Payment-Gateway-Schnittstelle mit einer Operation: diesen Betrag dieser Karte belasten. Er schreibt einen Adapter pro Anbieter. Der Checkout hängt nur von der Schnittstelle ab, also bedeutet ein Anbieterwechsel, einen neuen Adapter zu schreiben. Das erleichtert auch das Testen. In Tests kann ein gefälschtes Gateway, das nie eine echte Bank kontaktiert, das echte ersetzen.
Kaum jemand schreibt alles von Grund auf selbst. Eine Bibliothek ist Code, den jemand anderes geschrieben hat und den Ihr Code bei Bedarf aufruft, etwa eine Bibliothek für Datumsangaben oder zum Erzeugen von PDF-Dateien. Ihr Code behält die Kontrolle. Bei einem Framework ist es umgekehrt. Es liefert das Grundgerüst der Anwendung und ruft Ihren Code im richtigen Moment auf. Das nennt man Inversion of Control: Rufen Sie uns nicht an, wir rufen Sie an. Frameworks sparen viel Entwurfsarbeit, sind aber später meist schwer zu ersetzen.
Jede Bibliothek und jedes Framework, das Sie nutzen, ist eine Abhängigkeit, beschafft von einem Paketmanager wie pip für Python oder npm für JavaScript. Hier wählt eine Shop-App direkt drei Pakete. Doch diese Pakete bringen fünf weitere mit, sogenannte transitive Abhängigkeiten. Sie haben drei Pakete gewählt, hängen aber von acht ab. Jedes davon ist Code, den Sie nicht geschrieben haben, und er läuft mit allen Berechtigungen Ihres eigenen. Ein Fehler in einem davon, etwa in diesem Krypto-Paket, ist also ein Fehler in Ihrer App.
Warum ist all diese Struktur so wichtig? Weil der größte Teil der Kosten von Software auf Änderungen entfällt. Zwei Ideen beschreiben gute Struktur. Kopplung beschreibt, wie stark ein Teil von den Details eines anderen abhängt. Kohäsion beschreibt, wie gut der Inhalt eines Teils zusammengehört. Links greifen vier Module alle in die Daten der anderen. Eine Änderung an einem kann sich auf alle anderen ausbreiten. Rechts haben dieselben Module nur drei Verbindungen, jede über eine kleine Schnittstelle. Das ist das Ziel: lose Kopplung und hohe Kohäsion. Außerdem können so getrennte Teams unabhängig arbeiten und releasen.
Architektur ist dieselbe Tätigkeit in größerem Maßstab: die Hauptteile festlegen, ihre Schnittstellen und die Richtung der Abhängigkeiten. Der vollständige Abschnitt enthält den Code und eine Checkliste für das Hinzufügen von Abhängigkeiten. Als Nächstes, in Abschnitt null Punkt vier, verlässt der Code den einzelnen Computer, und wir sehen uns an, wie Computer kommunizieren.
Was Sie wissen müssen
- Eine Funktion ist ein benanntes, wiederverwendbares Stück Code mit Eingaben und einer Ausgabe; mit ihr lässt sich eine Regel einmal schreiben und an einer Stelle ändern.
- Module und Pakete fassen zusammengehörigen Code zusammen und trennen einen kleinen öffentlichen Teil von privaten Details, die sich frei ändern dürfen.
- Klassen bündeln Daten mit den Methoden, die darauf arbeiten; Kapselung hält die Daten privat, sodass die Regeln immer durchgesetzt werden.
- Eine Schnittstelle ist ein Vertrag zwischen Teilen; Code, der von einer Schnittstelle statt von einer Implementierung abhängt, kann Implementierungen leicht austauschen.
- Eine Bibliothek rufen Sie auf; ein Framework ruft Ihren Code auf. Diese Inversion of Control macht Frameworks leistungsfähig, aber schwerer zu ersetzen.
- Abhängigkeiten, die Paketmanager wie pip und npm beschaffen, sparen Aufwand, bringen aber transitiven Code, Sicherheitsrisiken und Wartungsarbeit mit sich.
- Gute Struktur bedeutet lose Kopplung und hohe Kohäsion; das hält Änderungen günstig und lässt Teams unabhängig arbeiten.
Warum Code überhaupt organisieren?
Ein kleines Skript mit zwanzig Zeilen lässt sich in einer Minute von oben nach unten lesen. Ein echtes System, etwa eine Essenslieferplattform, kann Hunderttausende oder Millionen Zeilen umfassen, geschrieben von Dutzenden Menschen über viele Jahre. Niemand kann das im Kopf behalten. Die einzige Möglichkeit, an einem solchen System zu arbeiten, besteht darin, es in Teile zu zerlegen, die sich einzeln verstehen, testen und ändern lassen, mit klaren Regeln dafür, wie die Teile einander nutzen dürfen.
Programmiersprachen bieten dafür eine Leiter von Bausteinen, vom Kleinen zum Großen: Funktionen, Module und Pakete, Klassen und Objekte und die Schnittstellen zwischen ihnen. Über dem Code, den wir selbst schreiben, liegen Bibliotheken und Frameworks, die andere geschrieben haben. Dieser Abschnitt steigt diese Leiter hinauf. Dieselben Ideen, in größerem Maßstab angewandt, sind der Gegenstand der Softwarearchitektur.
Funktionen: eine Aufgabe benennen und wiederverwenden
Eine Funktion ist ein benanntes Stück Code, das eine Aufgabe erledigt. Sie nimmt Eingaben entgegen (Parameter oder Argumente genannt), verrichtet ihre Arbeit und gibt meist eine Ausgabe zurück (ihren Rückgabewert). Denken Sie an einen Kaffeevollautomaten: Sie geben Bohnen, Wasser und eine Getränkewahl hinein; er erledigt seine Arbeit; heraus kommt ein Kaffee. Sie müssen nicht wissen, wie die Pumpe funktioniert, um ihn zu benutzen.
Funktionen bringen drei Vorteile. Wiederverwendung: Die Mehrwertsteuerberechnung wird einmal geschrieben und vom Checkout, von der Rechnung und von der Erstattungsmaske aufgerufen. Lesbarkeit: total_with_vat(basket) sagt, was geschieht, ohne zu zeigen, wie. Änderung an einer Stelle: Wenn sich der Mehrwertsteuersatz ändert, ändert sich eine Funktion, nicht zwanzig Kopien derselben Rechnung, verstreut über den Code (Kopien, die bald auseinanderlaufen würden).
Eine gute Funktion tut eine Sache, hat einen Namen, der sagt, was sie tut, erhält die benötigten Eingaben als Parameter, statt nach verborgenen Daten zu greifen, und ist kurz genug, um sie auf einen Blick zu erfassen. Eine Funktion, die ihre Ausgabe nur aus ihren Eingaben berechnet, ohne sonst etwas zu verändern, heißt reine Funktion; reine Funktionen sind besonders leicht zu testen, weil dieselben Eingaben immer dieselbe Ausgabe liefern.
VAT_RATE = 0.20
def total_with_vat(prices_in_pence):
"""Return the total of a basket in pence, including VAT."""
subtotal = sum(prices_in_pence)
return round(subtotal * (1 + VAT_RATE))
checkout_total = total_with_vat([1250, 399]) # 1979
invoice_total = total_with_vat([5000]) # 6000Module und Pakete: zusammengehörigen Code gruppieren
Ein Modul fasst zusammengehörige Funktionen und Daten zu einer Einheit mit eigenem Namen zusammen. In Python ist ein Modul einfach eine .py-Datei; pricing.py könnte alles enthalten, was mit Preisen, Rabatten und Steuern zu tun hat. Eine andere Datei nutzt es, indem sie import pricing schreibt. Ein Paket ist ein Ordner zusammengehöriger Module; ein Shop könnte also ein Paket shop mit den Modulen pricing, orders und customers haben. Andere Sprachen verwenden andere Begriffe (Java hat Packages, C# hat Namespaces und Assemblies, JavaScript hat Module), aber die Idee ist dieselbe.
Ein Modul ist wie eine Abteilung in einer Organisation. Die Finanzabteilung hat einen öffentlichen Empfang, an dem andere Anliegen vorbringen können („Bitte stellen Sie diese Rechnung aus“), und interne Abläufe, die niemand außerhalb sehen muss. Ebenso trennen Module einen kleinen öffentlichen Teil, den andere Module nutzen sollen, von privaten Details. Python kennzeichnet private Namen per Konvention mit einem führenden Unterstrich, etwa _round_pence; Java und C# erzwingen die Privatheit mit Schlüsselwörtern wie private. Der Wert dieser Trennung liegt darin, dass sich der private Teil frei neu schreiben lässt, ohne bei anderen etwas kaputt zu machen, solange der öffentliche Teil seine Versprechen hält.
Funktionen, Module und ein Paket
Klassen und Objekte: Daten mit ihrem Verhalten
Viele Sprachen erlauben es, Daten mit den Funktionen zu bündeln, die mit ihnen arbeiten. Eine Klasse ist die Vorlage; ein Objekt ist ein einzelnes Exemplar, das daraus erzeugt wird. Eine Klasse BankAccount könnte festlegen, dass jedes Konto einen Inhaber und einen Kontostand hat und Einzahlungen und Abhebungen annehmen kann. Das Konto jedes Kunden ist dann ein eigenes Objekt dieser Klasse. Funktionen, die zu einer Klasse gehören, heißen Methoden. (Siehe den Glossareintrag zu Klasse und Objekt.)
Die für die Architektur nützlichste Idee hierbei ist die Kapselung: Ein Objekt hält seine Daten privat und lässt anderen Code sie nur über seine Methoden ändern. Könnte jeder account.balance direkt setzen, würde nichts Code an anderer Stelle daran hindern, einen Kontostand negativ zu machen oder das Audit-Log zu umgehen. Mit Kapselung ist die Methode withdraw die einzige Tür, und sie kann die Regeln jedes Mal durchsetzen. Die Analogie ist ein Bankschalter: Kunden können nicht in den Tresorraum spazieren, aber sie können den Schalterbeamten fragen, der die Regeln prüft, bevor er Geld aushändigt.
Die objektorientierte Programmierung umfasst noch viel mehr (Vererbung, Polymorphie, Entwurfsmuster), und Fachleute sind uneins darüber, wie viel davon hilfreich ist: Manche Sprachen, etwa Go, verzichten bewusst auf Vererbung, und viele Teams bevorzugen einen eher funktionalen Stil. Kapselung ist jedoch in jedem Stil wertvoll.
class BankAccount:
def __init__(self, owner):
self.owner = owner
self._balance = 0 # private by convention
def deposit(self, pence):
if pence <= 0:
raise ValueError("Deposit must be positive")
self._balance += pence
def withdraw(self, pence):
if pence > self._balance:
raise ValueError("Insufficient funds")
self._balance -= pence
def balance(self):
return self._balance
acct = BankAccount("Ana")
acct.deposit(10000)
acct.withdraw(2500)
print(acct.balance()) # 7500Schnittstellen: Verträge zwischen Teilen
Eine Schnittstelle ist die vereinbarte Menge von Operationen, die ein Teil eines Systems anderen anbietet: ihre Namen, welche Eingaben sie erwarten, was sie zurückgeben und was schiefgehen kann. Sie ist ein Vertrag. Der Aufrufer verlässt sich auf den Vertrag, nicht darauf, wie die Arbeit erledigt wird. Eine Steckdose ist eine Schnittstelle: Jedes Gerät mit dem richtigen Stecker funktioniert, und dem Gerät ist es gleich, ob der Strom aus Wind, Gas oder Kernkraft stammt.
Im Code erlaubt eine Schnittstelle, eine Implementierung gegen eine andere auszutauschen. Angenommen, ein Shop wickelt Kartenzahlungen heute über einen Zahlungsanbieter ab, könnte aber im nächsten Jahr wechseln. Ruft der Checkout-Code überall direkt die eigenen Funktionen des Anbieters auf, bedeutet ein Wechsel, den Checkout neu zu schreiben. Definiert der Shop stattdessen eine eigene kleine Schnittstelle PaymentGateway mit einer Operation, charge(amount, card_token), und schreibt dann einen Adapter pro Anbieter, hängt der Checkout nur von der Schnittstelle ab, und ein Anbieterwechsel bedeutet, einen neuen Adapter zu schreiben. Derselbe Kniff erleichtert das Testen: In Tests kann ein gefälschtes Gateway, das nie eine echte Bank kontaktiert, das echte ersetzen.
Schnittstellen sind die wichtigste Idee dieses Abschnitts für Architekten. Jede Grenze in einem System, von einer Funktionssignatur bis zu einer Web-API, die Tausende anderer Unternehmen nutzen, ist eine Schnittstelle. Gute Schnittstellen sind klein und stabil und verbergen Entscheidungen, die sich wahrscheinlich ändern; dieses Prinzip, Information Hiding genannt, formulierte David Parnas 1972, und es wird in Kapitel 3 behandelt.
from typing import Protocol
class PaymentGateway(Protocol):
def charge(self, amount_pence: int, card_token: str) -> bool: ...
class ProviderAGateway:
def charge(self, amount_pence, card_token):
... # call Provider A's web API
return True
class FakeGateway: # used in tests
def charge(self, amount_pence, card_token):
return amount_pence < 100_000 # pretend large payments fail
def checkout(basket_total, card_token, gateway: PaymentGateway):
return "paid" if gateway.charge(basket_total, card_token) else "declined"Bibliotheken und Frameworks: Code, den andere geschrieben haben
Kaum jemand schreibt alles von Grund auf selbst. Eine Bibliothek ist wiederverwendbarer Code, den jemand anderes geschrieben hat und den Ihr Code bei Bedarf aufruft: eine Bibliothek für Datumsangaben und Zeitzonen, für das Erzeugen von PDFs, für den Zugriff auf eine Datenbank. Sie behalten die Kontrolle; die Bibliothek ist ein Werkzeug, das Sie zur Hand nehmen und wieder weglegen.
Ein Framework ist etwas anderes. Es liefert das Grundgerüst einer Anwendung und entscheidet, wann was geschieht; Sie füllen die Lücken mit Ihrem eigenen Code, und das Framework ruft diesen Code im richtigen Moment auf. Ein Web-Framework wie Django (Python), Spring (Java), Ruby on Rails oder Express (JavaScript) empfängt jede eingehende Webanfrage, ermittelt, welche Ihrer Funktionen sie bearbeiten soll, ruft diese Funktion auf und macht aus ihrem Ergebnis eine Webseite oder Daten. Die Kurzformel für den Unterschied: Eine Bibliothek rufen Sie auf; ein Framework ruft Ihren Code auf.
Diese Umkehrung heißt Inversion of Control. Martin Fowler schreibt die Verwendung des Begriffs im Jahr 1988 Ralph Johnson und Brian Foote zu, und die Idee wird oft als Hollywood-Prinzip zusammengefasst: Rufen Sie uns nicht an, wir rufen Sie an
. Ein Franchise-Restaurant ist eine treffende Analogie: Der Franchisegeber stellt das Gebäude, das Bestellsystem, die Öffnungszeiten und die Regeln; der Franchisenehmer liefert das Kochen in den Lücken, die der Franchisegeber vorsieht.
Wer ruft wen auf?
Bibliothek oder Framework
| Bibliothek | Framework | |
|---|---|---|
| Wer steuert den Ablauf | Ihr Code | Das Framework (Inversion of Control) |
| Beispiele | Eine Datumsbibliothek, ein PDF-Generator, ein JSON-Parser | Django, Spring, Ruby on Rails, Angular |
| Wie leicht später zu ersetzen | Meist mäßig: die Stellen ändern, die sie aufrufen | Meist schwer: Die Struktur des Frameworks prägt die ganze Anwendung |
| Hauptnutzen | Erspart, eine Fähigkeit selbst zu schreiben | Erspart den Entwurf der gesamten Struktur; einheitliche Konventionen im ganzen Team |
| Hauptrisiko | Eine weitere Abhängigkeit, die gepflegt werden muss | Lock-in an seine Arbeitsweise und seinen Upgrade-Zyklus |
Abhängigkeiten und Paketmanager
Jede Bibliothek und jedes Framework, das Ihr Code benötigt, ist eine Abhängigkeit. Moderne Projekte haben viele davon und nutzen daher einen Paketmanager, um sie zu beschaffen: pip für Python (aus dem Python Package Index, PyPI) und npm für JavaScript (aus der npm-Registry) sind die bekanntesten, daneben gibt es Entsprechungen wie Maven für Java und NuGet für .NET. Ein Projekt listet auf, was es braucht, oft mit Versionsbereichen, und der Paketmanager lädt diese Pakete herunter und, ganz entscheidend, auch die Pakete, von denen sie abhängen. Diese indirekten, transitiven Abhängigkeiten übertreffen die direkten oft um ein Vielfaches.
Versionen werden üblicherweise nach dem Schema MAJOR.MINOR.PATCH nummeriert, gemäß einer Konvention namens semantische Versionierung: Eine Änderung der Major-Nummer signalisiert eine inkompatible Änderung, eine Minor-Nummer neue Funktionen, die bestehende Nutzer nicht beeinträchtigen sollten, und eine Patch-Nummer Fehlerbehebungen. Projekte führen außerdem ein Lockfile (etwa package-lock.json für npm), das die genaue Version jedes installierten Pakets festhält, damit der Rechner jedes Entwicklers und der Produktions-Build denselben Code verwenden.
Direkte und transitive Abhängigkeiten
Abhängigkeiten ersparen enormen Aufwand, aber jede ist Code, den Sie nicht geschrieben haben und der mit allen Berechtigungen Ihres eigenen Codes läuft. Zwei bekannte Vorfälle zeigen die Risiken. Im März 2016 entfernte ein Entwickler im Zuge eines Streits seine Pakete aus npm, darunter left-pad, eine winzige Funktion von etwa elf Zeilen zum Auffüllen von Text; Tausende Projekte, die direkt oder indirekt davon abhingen, ließen sich plötzlich nicht mehr bauen, bis npm das Paket wiederherstellte. Im Dezember 2021 wurde in Log4j 2, einer weit verbreiteten Java-Logging-Bibliothek, eine schwere Sicherheitslücke mit dem Spitznamen Log4Shell bekannt; Organisationen in aller Welt verbrachten Wochen damit, jedes System zu finden, das sie enthielt, oft mehrere Ebenen tief in ihren Abhängigkeiten vergraben.
Checkliste: bevor Sie eine Abhängigkeit hinzufügen
Fragen, die ein sorgfältiges Team stellt, bevor es sich auf das Paket eines anderen verlässt.
- Brauchen wir es?Ein paar Zeilen eigener Code können auf Dauer günstiger sein als ein Paket, das verfolgt und aktualisiert werden muss.
- Wird es gepflegt?Achten Sie auf aktuelle Releases, offene Sicherheitsprobleme und darauf, wie viele Personen es pflegen. Ein verwaistes Paket wird zu Ihrem Problem.
- Was bringt es mit?Prüfen Sie, wie viele transitive Abhängigkeiten es hinzufügt; jede ist weiterer Code, dem man vertrauen muss.
- Ist die Lizenz kompatibel?Open-Source-Lizenzen unterscheiden sich; manche bringen Pflichten mit sich, wenn Sie Ihre Software weitergeben.
- Sind die Versionen fixiert und geprüft?Verwenden Sie ein Lockfile und ein automatisiertes Werkzeug, das warnt, wenn eine bekannte Schwachstelle eine von Ihnen genutzte Version betrifft.
- Könnten wir es ersetzen?Kapseln Sie wichtige Abhängigkeiten hinter einer eigenen Schnittstelle, sodass ein Anbieterwechsel nur eine Stelle berührt.
Warum Struktur wichtig ist: Änderungen und Teams
Code wird weit häufiger gelesen und geändert, als er anfangs geschrieben wird. Der größte Teil der Lebenszeitkosten von Software entfällt auf Änderungen: Fehler beheben, Features hinzufügen, sich an neue Gesetze und neue Partner anpassen. Die Struktur entscheidet, wie teuer das ist. In einem gut organisierten System berührt eine Änderung an der Mehrwertsteuerberechnung ein Modul. In einem schlecht organisierten berührt sie vierzig Dateien, die fünf Teams gehören, und irgendetwas, das nichts damit zu tun hat, geht kaputt.
Zwei Ideen, erstmals beschrieben von Larry Constantine und Kollegen in der Structured-Design-Bewegung der späten 1960er- und der 1970er-Jahre, erfassen, was „gut organisiert“ bedeutet. Kopplung beschreibt, wie stark ein Teil von den Details eines anderen abhängt: In einem eng gekoppelten System erzwingt die Änderung eines Teils Änderungen an anderen. Kohäsion beschreibt, wie gut der Inhalt eines Teils zusammengehört: Ein Modul mit hoher Kohäsion erledigt eine klare Aufgabe. Das Ziel ist lose Kopplung und hohe Kohäsion: Teile, die jeweils eine Aufgabe gut erledigen und über kleine, stabile Schnittstellen miteinander sprechen.
Struktur ist auch für Menschen wichtig. Müssen zwei Teams für den Großteil ihrer Arbeit dasselbe Modul bearbeiten, werden sie ständig aufeinander warten und sich gegenseitig etwas kaputt machen. Besitzt jedes Team Module mit hoher Kohäsion und klaren Schnittstellen, kann es unabhängig arbeiten und releasen. Melvin Conway beobachtete 1968, dass Systeme dazu neigen, die Kommunikationsstruktur der Organisationen abzubilden, die sie bauen; diese Beobachtung ist als Gesetz von Conway bekannt und wird in Kapitel 3 untersucht.
Kopplung auf einen Blick
Warum das für die Architektur wichtig ist
Architektur ist weitgehend dieselbe Tätigkeit wie in diesem Abschnitt, nur in größerem Maßstab und mit höherem Einsatz: entscheiden, was die Hauptteile eines Systems sind, wofür jedes zuständig ist, welche Schnittstellen sie verbinden und in welche Richtung die Abhängigkeiten zeigen. Ein Modul innerhalb eines Programms und ein separat deployter Service jenseits eines Netzwerks sind beide Teile mit Schnittstellen; die Fragen nach Kopplung, Kohäsion und Information Hiding gelten für beide.
Kapitel 3 macht diese Prinzipien präzise, beginnend mit Modularität und Kopplung und Kohäsion, und Kapitel 8 greift Abhängigkeiten als Sicherheitsthema wieder auf, in Sicherheit der Software-Lieferkette. Als Nächstes verlässt der Code den einzelnen Computer: Der folgende Abschnitt erklärt, wie Computer miteinander kommunizieren.
Prüfen Sie Ihr Verständnis
Warum ist es besser, eine Funktion total_with_vat von drei Stellen aus aufzurufen, als die Berechnung dreimal zu schreiben?
Die Regel steht dann an einer Stelle: Ändert sich der Mehrwertsteuersatz, korrigiert eine Änderung jeden Aufrufer, und die Kopien können nicht auseinanderlaufen.
Woran hindert die Kapselung anderen Code bei einem Bankkonto-Objekt?
Sie verhindert, dass anderer Code den Kontostand direkt ändert, sodass jede Änderung über Methoden wie withdraw laufen muss, die die Regeln durchsetzen.
Ein Web-Framework ruft Ihre Funktion auf, sobald eine Anfrage für /orders eintrifft. Wie nennt man diese Anordnung?
Inversion of Control: Das Framework, nicht Ihr Code, steuert den Ablauf und ruft Ihren Code bei Bedarf auf.
Wozu dient ein Lockfile wie package-lock.json?
Es hält die genaue Version jedes installierten Pakets fest, auch der indirekten, sodass jeder Entwicklerrechner und jeder Produktions-Build genau denselben Code verwenden.
Quellen und weiterführende Literatur
Haben Sie eine Frage zu diesem Abschnitt? ArchitectBot, der KI-Lernassistent des Kurses, antwortet zuerst aus dem Kurs.
Fragen: „Wie entscheide ich, ob ich ein Feature auf einem Framework aufbaue oder meine Geschäftslogik davon unabhängig halte?“