Das Delegatenmuster ist eines der wichtigsten Entwurfsmuster.
Nehmen wir an, wir entwickeln eine Friseuranwendung. Die Anwendung verfügt über einen Kalender zum Auswählen eines Tages für die Aufzeichnung; durch Tippen auf das Datum sollte eine Liste mit Friseuren mit einer Auswahl geöffnet werden.
Lassen Sie uns eine naive Verknüpfung von Systemkomponenten implementieren, Kalender und Bildschirm mithilfe von Zeigern aufeinander kombinieren, um eine Listenanzeige zu implementieren:
// псевдокод
class BarbershopScreen {
let calendar: Calendar
func showBarbersList(date: Date) {
showSelectionSheet(barbers(forDate: date))
}
}
class Calendar {
let screen: BarbershopScreen
func handleTap(on date: Date) {
screen.showBarbersList(date: date)
}
}
Nach ein paar Tagen ändern sich die Anforderungen; vor der Anzeige der Liste müssen Sie Angebote mit einer Auswahl an Dienstleistungen (Bartschneiden usw.) anzeigen, jedoch nicht immer, an allen Tagen außer Samstag.
Wir fügen dem Kalender eine Prüfung hinzu, ob Samstag ist oder nicht. Abhängig davon nennen wir die Methode der Liste der Friseure oder der Liste der Dienstleistungen. Der Übersichtlichkeit halber werde ich Folgendes demonstrieren:
// псевдокод
class BarbershopScreen {
let calendar: Calendar
func showBarbersList(date: Date) {
showSelectionSheet(barbers(forDate: date))
}
func showOffersList() {
showSelectionSheet(offers)
}
}
class Calendar {
let screen: BarbershopScreen
func handleTap(on date: Date) {
if date.day != .saturday {
screen.showOffersList()
}
else {
screen.showBarbersList(date: date)
}
}
}
Eine Woche später werden wir gebeten, einen Kalender zum Feedback-Bildschirm hinzuzufügen, und in diesem Moment passiert das erste architektonische Ups!
Was zu tun? Der Kalender ist eng mit dem Friseurtermin-Bildschirm verknüpft.
Wow! Pfui! oh-oh
Wenn Sie weiterhin mit dieser verrückten Anwendungsarchitektur arbeiten, sollten Sie eine Kopie der gesamten Kalenderklasse erstellen und diese Kopie mit dem Feedback-Bildschirm verknüpfen.
Ok, sieht gut aus, dann haben wir noch ein paar Bildschirme und mehrere Kopien des Kalenders hinzugefügt, und dann war es soweit. Wir wurden gebeten, das Design des Kalenders zu ändern, was bedeutet, dass wir jetzt alle Kopien des Kalenders finden und bei allen die gleichen Änderungen vornehmen müssen. Dieser „Ansatz“ hat großen Einfluss auf die Entwicklungsgeschwindigkeit und erhöht die Wahrscheinlichkeit, einen Fehler zu machen. Dies hat zur Folge, dass solche Projekte im Chaos enden, wenn selbst der Autor der ursprünglichen Architektur nicht mehr versteht, wie Kopien seiner Klassen funktionieren, und andere im Laufe der Zeit hinzugefügte Hacks plötzlich auseinanderfallen.
Was musste getan werden, oder noch besser, womit konnte man noch nicht zu spät beginnen? Verwenden Sie das Delegationsmuster!
Die Delegation ist eine Möglichkeit, Klassenereignisse über eine gemeinsame Schnittstelle weiterzuleiten. Unten finden Sie ein Beispiel für einen Delegaten für einen Kalender:
protocol CalendarDelegate {
func calendar(_ calendar: Calendar, didSelect date: Date)
}
Jetzt fügen wir dem Beispielcode den Code für die Arbeit mit dem Delegaten hinzu:
// псевдокод
class BarbershopScreen: CalendarDelegate {
let calendar: Calendar
init() {
calendar.delegate = self
}
func calendar(_ calendar: Calendar, didSelect date: Date) {
if date.day != .saturday {
showOffersList()
}
else {
showBarbersList(date: date)
}
}
func showBarbersList(date: Date) {
showSelectionSheet(barbers(forDate: date))
}
func showOffersList() {
showSelectionSheet(offers)
}
}
class Calendar {
weak var delegate: CalendarDelegate
func handleTap(on date: Date) {
delegate?.calendar(self, didSelect: date)
}
}
Daher haben wir den Kalender vollständig vom Bildschirm entkoppelt. Bei der Auswahl eines Datums aus dem Kalender wird das Datumsauswahlereignis – *delegiert* die Ereignisverarbeitung an den Abonnenten; Der Abonnent ist der Bildschirm.
Welche Vorteile ergeben sich für uns aus diesem Ansatz? Jetzt können wir die Kalender- und Bildschirmlogik unabhängig voneinander ändern, ohne Klassen zu duplizieren, was die weitere Unterstützung vereinfacht; Dadurch wird das „Prinzip der Alleinverantwortung“ für die Umsetzung von Systemkomponenten umgesetzt und das DRY-Prinzip eingehalten.
Wenn Sie die Delegation verwenden, können Sie die Logik für die Anzeige von Fenstern und die Reihenfolge von allem auf dem Bildschirm hinzufügen und ändern. Dies hat keinerlei Auswirkungen auf den Kalender und andere Klassen, die objektiv nicht an Prozessen teilnehmen sollten, die nicht direkt mit ihnen zusammenhängen.< br />Alternativ können Programmierer, die sich nicht allzu sehr darum kümmern, Nachrichten über einen gemeinsamen Bus senden, ohne eine separate Protokoll-/Delegiertenschnittstelle zu schreiben, wo es besser wäre, die Delegation zu verwenden. Ich habe in einem früheren Beitrag über die Nachteile dieses Ansatzes geschrieben – „Beobachtermuster.“
Quellen
https://refactoring.guru/ru/replace-inheritance -with-delegation