Jede Klasse sollte nur eine Verantwortung haben (Unix-Philosophie)
Grund: steigende Änderungswahrscheinlichkeit führt zu steigender Fehlerwahrscheinlichkeit
Klassen sollten für Erweiterungen offen sein, gleichzeitig aber ihr Verhalten nicht ändern. (open for extension, closed for modification)
Beispiel: Vererbung ändert Basisverhalten nicht, erweitert aber um zusätzliche Funktionen oder Daten.
Instanzen von abgeleiteten Klassen müssen sich so verhalten, dass sie das Verhalten der Basisklasse 1:1 abbilden können. Überschriebene Methoden dürfen ihr Basisverhalten nicht ändern.
Ziel: Vermeidung von Überraschungen beim Anwender.
Clients sollten nicht dazu gezwungen werden, von Interfaces abzuhängen, die sie nicht verwenden. Clients agieren nur mit Interfaces, die das und nur das implementieren, was die Clients benötigen.
Ziel: Aufteilung großer Interfaces und Reduktion unnötiger Abhängigkeiten.
Abhängigkeiten sollten von kronkreteren Modulen niedrigerer Ebenen zu abstrakten Modulen höherer Ebenen gerichtet sein.
Ziel: Reduktion von Abhängigkeiten zwischen Modulen.
Objekt sucht nach benötigtem Objekt, beispielsweise in einem Register. Damit hängt es nicht von jedem benötigten Objekt ab, sondern nur vom Register. Das Objekt stellt zur Laufzeit seine Verknüpfung mit einem anderen Objekt her, indem es nach einem logischen Namen sucht. (Aktiv)
Beispiel: Person ruft Escort-Service an und fragt gezielt nach "Scarlett O'Hara".
Erzeugung von Objekten und Zuordnungen von Abhängigkeiten werden an eine dritte Partei delegiert, damit sind die Objekte voneinander nicht abhängig. Abhängigkeiten werden von außen injiziert. (Passiv)
Beispiel: Der Escort-Service weiß, was seine Kunden brauchen und teilt diesen ohne Nachfragen oder Auftrag eine Escort-Dame zu.
Eine Klasse mit "strong cohesion" hat wenige, wohldefinierte Aufgaben, die eng miteinander verwandt sind.
Beispiel: High Cohesion: Escort-Dame. Low Cohesion: Partnerin.
Komponenten einer Software kommunizieren nur über wenige Schnittstellen mit anderen Komponenten, dadurch ist die Abhängigkeit sehr gering. Globale Variablen, öffentliche Attribute, Singletons, Zustände in Datenbanken: Schlecht, da sie Schnittstellen vergrößern. // TODO: Bild strong cohesion/loose coupling
Architekturmuster beziehen sich auf das Gesamtsystem (grundlegendes Problem), während sich Entwurfsmuster auf die Teilgebiete und die Umsetzung beziehen (lokales Problem)
Beispiel: Einkommensgenerierung. Architektur: Gründung Escort-Service. Entwurf: Standort, Personal, Verwaltung.
Software ist in logischen Schichten organisiert. Problem:
- Unterteilung komplexer Systeme (Abhängigkeitsminimierung)
- Horizontale Strukturierung (Obere Schicht = Client, Untere Schicht = Server)
- Jede Schicht repräsentiert eine Abstraktion
Lösung:
- Jede Schicht verfolgt einen bestimmten Zweck
- Client-Server Modell
// TODO: Bild Layers
Schichten dürfen nicht übersprungen werden und dürfen nur die direkt darunter liegende Schicht aufrufen.
Vorteil: leichter test- und wartbar.
Schichten dürfen übersprungen werden, höchste Schicht kann direkt mit tiefster Schicht kommunizieren.
Vorteil: höhere Performanz.
Filter modifizieren Daten, Pipes transportieren Daten. Problem:
- Unterteilung Datenstrom in Einzelschritte
- Einfach erweiterbar
- Parallelverarbeitung gut möglich
Lösung:
- Zerlegung in Einzelschritte
- Jeder Verarbeitungsschritt ist ein Filter
- Jeder Transportweg ist eine Pipe
- Filter lesen, verarbeiten und geben Daten aus
- Pipes transportieren und puffern Daten
- Pipes entkoppeln Entitäten
- Asynchrone Schreib- und Lesevorgänge möglich
// TODO: Bild Pipes / Filters
Eigenständig laufende Prozesse oder Threads, werden nach Instanziierung von übergeordnetem Programmteil aufgerufen. Laufen kontinuierlich.
Aufruf durch benachbartes Filterelement.
Vorgänger hat Ausgabedaten erzeugt und ruft zu aktivierenden Filter auf. (Daten werden zur Verfügung gestellt)
Nachfolger fordert Daten an. (Daten werden benötigt)
Interface beschreibt Funktion, Plug-Ins sind die konkreten Umsetzungen dieser Interfaces. Der Plugin-Manager sorgt für die korrekte Zuordnung der konkreten Umsetzung zum Interface bei Aufruf.
Problem:
- Erweiterbarkeit ohne Modifikation
- Neue Funktionalität durch Dritte
- Core-System funktioniert ohne Zusätze, bietet mit ihnen aber mehr Funktionalität
Lösung:
- Interfaces für Erweiterungen
- Plug-Ins implementieren diese
- Plug-Ins ersetzen zur Laufzeit die Interfaces durch konkrete Implementierungen.
Beispiel: Interface: Escort-Dame, Konkrete Umsetzungen: Cindy, Mandy, Scarlett, Plugin-Manager: Telefonistin der Agentur.
// TODO: Bild Plug-In
Broker ist verantwortlich für die Koordination von Kommunikation (Weitergabe von Anfragen und Antworten)
Problem:
- Skalierbarkeit
- Verteilbarkeit
- Lose Kopplung
- Gegenseitige Abhängigkeit nicht sinnvoll
Lösung:
- Komponenten klassifiziert durch deren Rolle
- Serverkomponenten bieten n>=1 Services
- Clients nutzen m>=1 Services
- Rollen können sich ändern
- Broker bietet Vermittlungs- und Zwischenschicht
Serialisierung von Methodenaufrufen für Netzwerktransmission
Rückkonvertierung in Standard-Methodenaufrufe auf Empfängerseite
Beispiel: Pornokino mit Gloryhole. Die Wand mit dem Loch ist der Broker, die Kundschaft sind die Clients, die Dienstleisterinnen sind die für den Client unzugänglichen Systeme und unterscheiden und/oder ergänzen sich im Funktionsumfang.
// TODO: Bild Broker
// TODO: Bild SOA
Problem: Lösung: // TODO: Bild MVC
Speichert Daten.
Verantwortlich für jegliche Art der Darstellung, kann lesend auf Model zugreifen.
Model sendet aktiv Daten an die View, welche dann die Darstellung aktualisiert.
View wird durch Model informiert, dass neue Daten vorhanden sind. Wird die View immer informiert ist es aktiver Pull, sonst passiver. Der Controller wertet Benutzereingaben aus und aktualisiert ggf. das Model oder die View. Die View zieht sich die neuen Daten nach Information durch das Model.
Verarbeitet Nutzeranfragen, kann lesend und schreibend auf Model zugreifen.
Wenn Methoden auf eine bestimmte Art implementiert werden müssen oder wenn non-public Daten oder Methoden enthalten sein sollen. Dies beeinträchtigt nicht die Möglichkeit, reine Methodenköpfe zu definieren, die überschrieben werden müssen.
Reine Schnittstellenbeschreibung.
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild
Problem: Lösung: // TODO: Bild