Das Mediator-Muster bezieht sich auf Verhaltensdesignmuster.
Eines Tages erhalten Sie den Auftrag, eine Scherzanwendung zu entwickeln – Der Benutzer drückt eine Taste in der Mitte des Bildschirms und er ertönt das lustige Quaken einer Ente.
Nach dem Hochladen in den App Store wird die Anwendung zum Hit: Alle quakten über Ihre Anwendung, Elon Musk quakt auf seinem Instagram beim nächsten Start eines Super-Hochgeschwindigkeitstunnels auf dem Mars, Hillary Clinton quält Donald Trump bei der Debatte und gewinnt die Wahlen in der Ukraine, Erfolg!
Die naive Implementierung der Anwendung sieht folgendermaßen aus:
class DuckButton {
func didPress() {
print("quack!")
}
}
let duckButton = DuckButton()
duckButton.didPress()
Als nächstes entscheiden Sie sich, das Geräusch eines Hundegebells hinzuzufügen. Dazu müssen Sie zwei Schaltflächen zur Auswahl des Geräuschs anzeigen – mit einer Ente und einem Hund. Lassen Sie uns zwei Schaltflächenklassen erstellen: DuckButton und DogButton.
Ändern Sie den Code:
class DuckButton {
func didPress() {
print("quack!")
}
}
class DogButton {
func didPress() {
print("bark!")
}
}
let duckButton = DuckButton()
duckButton.didPress()
let dogButton = DogButton()
dogButton.didPress()
Nach einem weiteren Erfolg fügen wir den Klang eines Schweinequietschens hinzu, jetzt gibt es drei Klassen von Tasten:
class DuckButton {
func didPress() {
print("quack!")
}
}
class DogButton {
func didPress() {
print("bark!")
}
}
class PigButton {
func didPress() {
print("oink!")
}
}
let duckButton = DuckButton()
duckButton.didPress()
let dogButton = DogButton()
dogButton.didPress()
let pigButton = PigButton()
pigButton.didPress()
Benutzer beschweren sich darüber, dass sich die Geräusche überlappen.
Wir fügen eine Prüfung hinzu, um dies zu verhindern, und stellen gleichzeitig die Klassen einander vor:
class DuckButton {
var isMakingSound = false
var dogButton: DogButton?
var pigButton: PigButton?
func didPress() {
guard dogButton?.isMakingSound ?? false == false &&
pigButton?.isMakingSound ?? false == false else { return }
isMakingSound = true
print("quack!")
isMakingSound = false
}
}
class DogButton {
var isMakingSound = false
var duckButton: DuckButton?
var pigButton: PigButton?
func didPress() {
guard duckButton?.isMakingSound ?? false == false &&
pigButton?.isMakingSound ?? false == false else { return }
isMakingSound = true
print("bark!")
isMakingSound = false
}
}
class PigButton {
var isMakingSound = false
var duckButton: DuckButton?
var dogButton: DogButton?
func didPress() {
guard duckButton?.isMakingSound ?? false == false &&
dogButton?.isMakingSound ?? false == false else { return }
isMakingSound = true
print("oink!")
isMakingSound = false
}
}
let duckButton = DuckButton()
duckButton.didPress()
let dogButton = DogButton()
dogButton.didPress()
let pigButton = PigButton()
pigButton.didPress()
Aufgrund des Erfolgs Ihres Antrags beschließt die Regierung, ein Gesetz zu erlassen, nach dem das Quacksalbern, Bellen und Grunzen auf Mobilgeräten an den restlichen Wochentagen nur von 9:00 bis 15:00 Uhr erfolgen darf Zu diesem Zeitpunkt riskiert der Benutzer Ihrer Anwendung eine Gefängnisstrafe von 5 Jahren wegen obszöner Tonproduktion mit persönlichen elektronischen Geräten.
Ändern Sie den Code:
import Foundation
extension Date {
func mobileDeviceAllowedSoundTime() -> Bool {
let hour = Calendar.current.component(.hour, from: self)
let weekend = Calendar.current.isDateInWeekend(self)
let result = hour >= 9 && hour <= 14 && weekend == false
return result
}
}
class DuckButton {
var isMakingSound = false
var dogButton: DogButton?
var pigButton: PigButton?
func didPress() {
guard dogButton?.isMakingSound ?? false == false &&
pigButton?.isMakingSound ?? false == false &&
Date().mobileDeviceAllowedSoundTime() == true else { return }
isMakingSound = true
print("quack!")
isMakingSound = false
}
}
class DogButton {
var isMakingSound = false
var duckButton: DuckButton?
var pigButton: PigButton?
func didPress() {
guard duckButton?.isMakingSound ?? false == false &&
pigButton?.isMakingSound ?? false == false &&
Date().mobileDeviceAllowedSoundTime() == true else { return }
isMakingSound = true
print("bark!")
isMakingSound = false
}
}
class PigButton {
var isMakingSound = false
var duckButton: DuckButton?
var dogButton: DogButton?
func didPress() {
guard duckButton?.isMakingSound ?? false == false &&
dogButton?.isMakingSound ?? false == false &&
Date().mobileDeviceAllowedSoundTime() == true else { return }
isMakingSound = true
print("oink!")
isMakingSound = false
}
}
let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()
duckButton.dogButton = dogButton
duckButton.pigButton = pigButton
dogButton.duckButton = duckButton
dogButton.pigButton = pigButton
pigButton.duckButton = duckButton
pigButton.dogButton = dogButton
duckButton.didPress()
dogButton.didPress()
pigButton.didPress()
Plötzlich fängt die Taschenlampen-Anwendung an, unsere vom Markt zu verdrängen. Lassen wir uns davon nicht unterkriegen und fügen wir eine Taschenlampe hinzu, indem wir die „oink-oink“-Taste und die restlichen Tasten drücken:
import Foundation
extension Date {
func mobileDeviceAllowedSoundTime() -> Bool {
let hour = Calendar.current.component(.hour, from: self)
let weekend = Calendar.current.isDateInWeekend(self)
let result = hour >= 9 && hour <= 14 && weekend == false
return result
}
}
class Flashlight {
var isOn = false
func turn(on: Bool) {
isOn = on
}
}
class DuckButton {
var isMakingSound = false
var dogButton: DogButton?
var pigButton: PigButton?
var flashlight: Flashlight?
func didPress() {
flashlight?.turn(on: true)
guard dogButton?.isMakingSound ?? false == false &&
pigButton?.isMakingSound ?? false == false &&
Date().mobileDeviceAllowedSoundTime() == true else { return }
isMakingSound = true
print("quack!")
isMakingSound = false
}
}
class DogButton {
var isMakingSound = false
var duckButton: DuckButton?
var pigButton: PigButton?
var flashlight: Flashlight?
func didPress() {
flashlight?.turn(on: true)
guard duckButton?.isMakingSound ?? false == false &&
pigButton?.isMakingSound ?? false == false &&
Date().mobileDeviceAllowedSoundTime() == true else { return }
isMakingSound = true
print("bark!")
isMakingSound = false
}
}
class PigButton {
var isMakingSound = false
var duckButton: DuckButton?
var dogButton: DogButton?
var flashlight: Flashlight?
func didPress() {
flashlight?.turn(on: true)
guard duckButton?.isMakingSound ?? false == false &&
dogButton?.isMakingSound ?? false == false &&
Date().mobileDeviceAllowedSoundTime() == true else { return }
isMakingSound = true
print("oink!")
isMakingSound = false
}
}
let flashlight = Flashlight()
let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()
duckButton.dogButton = dogButton
duckButton.pigButton = pigButton
duckButton.flashlight = flashlight
dogButton.duckButton = duckButton
dogButton.pigButton = pigButton
dogButton.flashlight = flashlight
pigButton.duckButton = duckButton
pigButton.dogButton = dogButton
pigButton.flashlight = flashlight
duckButton.didPress()
dogButton.didPress()
pigButton.didPress()
Als Ergebnis haben wir eine riesige Anwendung, die viel Copy-Paste-Code enthält, die Klassen darin sind durch einen toten Link miteinander verbunden – es gibt keine schwache Kopplung, ein solches Wunder ist sehr schwer zu warten und Änderungen in der Zukunft aufgrund der hohen Wahrscheinlichkeit, dass ein Fehler gemacht wird.
Verwenden Sie den Mediator
Fügen wir eine mittlere Mediatorklasse hinzu – ApplicationController. Diese Klasse sorgt für eine lose Kopplung von Objekten, gewährleistet die Trennung der Verantwortlichkeiten zwischen den Klassen und eliminiert doppelten Code.
Schreiben wir um:
import Foundation
class ApplicationController {
private var isMakingSound = false
private let flashlight = Flashlight()
private var soundButtons: [SoundButton] = []
func add(soundButton: SoundButton) {
soundButtons.append(soundButton)
}
func didPress(soundButton: SoundButton) {
flashlight.turn(on: true)
guard Date().mobileDeviceAllowedSoundTime() &&
isMakingSound == false else { return }
isMakingSound = true
soundButton.didPress()
isMakingSound = false
}
}
class SoundButton {
let soundText: String
init(soundText: String) {
self.soundText = soundText
}
func didPress() {
print(soundText)
}
}
class Flashlight {
var isOn = false
func turn(on: Bool) {
isOn = on
}
}
extension Date {
func mobileDeviceAllowedSoundTime() -> Bool {
let hour = Calendar.current.component(.hour, from: self)
let weekend = Calendar.current.isDateInWeekend(self)
let result = hour >= 9 && hour <= 14 && weekend == false
return result
}
}
let applicationController = ApplicationController()
let pigButton = SoundButton(soundText: "oink!")
let dogButton = SoundButton(soundText: "bark!")
let duckButton = SoundButton(soundText: "quack!")
applicationController.add(soundButton: pigButton)
applicationController.add(soundButton: dogButton)
applicationController.add(soundButton: duckButton)
pigButton.didPress()
dogButton.didPress()
duckButton.didPress()
Viele Artikel über Anwendungsarchitekturen für Benutzeroberflächen beschreiben das MVC-Muster und seine Ableitungen. Das Modell wird für die Arbeit mit Geschäftslogikdaten verwendet. Die Ansicht oder Präsentation zeigt dem Benutzer Informationen in der Schnittstelle an bzw. sorgt für die Interaktion mit dem Benutzer. Der Controller ist ein Vermittler, der die Interaktion der Systemkomponenten sicherstellt.
Quellen
https://refactoring.guru/ru/design-patterns/ Vermittler
Quellcode
https://gitlab.com/demensdeum/patterns/< /p>