Suche…


Einführung

Entwurfsmuster sind allgemeine Lösungen für Probleme, die häufig in der Softwareentwicklung auftreten. Im Folgenden finden Sie Mustervorlagen für standardisierte Best Practices beim Strukturieren und Entwerfen von Code sowie Beispiele für gängige Kontexte, in denen diese Entwurfsmuster geeignet wären.

Kreationelle Entwurfsmuster abstrahieren die Instantiierung von Objekten, um ein System unabhängiger von der Erstellung, Komposition und Repräsentation zu machen.

Singleton

Singletons sind ein häufig verwendetes Entwurfsmuster, das aus einer einzelnen Instanz einer Klasse besteht, die in einem Programm gemeinsam genutzt wird.

Im folgenden Beispiel erstellen wir eine static Eigenschaft, die eine Instanz der Foo Klasse enthält. Denken Sie daran, dass eine static Eigenschaft von allen Objekten einer Klasse gemeinsam genutzt wird und nicht durch Unterklassen überschrieben werden kann.

public class Foo
{
    static let shared = Foo()
    
    // Used for preventing the class from being instantiated directly
    private init() {}
    
    func doSomething()
    {
        print("Do something")
    }
}

Verwendungszweck:

Foo.shared.doSomething()

Denken Sie daran, sich an den private Initialisierer zu erinnern:

Dies stellt sicher, dass Ihre Singletons wirklich einzigartig sind, und verhindert, dass Objekte von außen mithilfe der Zugriffssteuerung eigene Instanzen Ihrer Klasse erstellen. Da alle Objekte mit einem öffentlichen Standardinitialisierer in Swift geliefert werden, müssen Sie Ihre Init-Instanz überschreiben und als privat kennzeichnen. KrakenDev

Fabrikmethode

Bei der klassenbasierten Programmierung ist das Factory-Method-Pattern ein Erzeugungsmuster, das Factory-Methoden verwendet, um das Problem beim Erstellen von Objekten zu lösen, ohne die genaue Klasse des zu erstellenden Objekts angeben zu müssen. Wikipedia-Referenz

protocol SenderProtocol
{
    func send(package: AnyObject)
}

class Fedex: SenderProtocol
{
    func send(package: AnyObject)
    {
        print("Fedex deliver")
    }
}

class RegularPriorityMail: SenderProtocol
{
    func send(package: AnyObject)
    {
        print("Regular Priority Mail deliver")
    }
}

// This is our Factory
class DeliverFactory
{
    // It will be responsable for returning the proper instance that will handle the task
    static func makeSender(isLate isLate: Bool) -> SenderProtocol
    {
        return isLate ? Fedex() : RegularPriorityMail()
    }
}

// Usage:
let package = ["Item 1", "Item 2"]

// Fedex class will handle the delivery
DeliverFactory.makeSender(isLate:true).send(package)

// Regular Priority Mail class will handle the delivery
DeliverFactory.makeSender(isLate:false).send(package)

Auf diese Weise sind wir nicht von der tatsächlichen Implementierung der Klasse abhängig und machen den sender() vollständig transparent für den Konsumenten.

In diesem Fall müssen wir nur wissen, dass ein Absender die Zustellung übernimmt und eine Methode namens send() verfügbar macht. Es gibt mehrere weitere Vorteile: Reduzieren Sie die Klassenkopplung, vereinfachen Sie das Testen, und fügen Sie neue Verhaltensweisen leichter hinzu, ohne ändern zu müssen, wer sie konsumiert.

Innerhalb des objektorientierten Designs bieten Schnittstellen Abstraktionsebenen, die die konzeptionelle Erklärung des Codes erleichtern und eine Barriere schaffen, die Abhängigkeiten verhindert. Wikipedia-Referenz

Beobachter

Das Beobachtermuster ist das Objekt, in dem ein Objekt, das als Subjekt bezeichnet wird, eine Liste seiner abhängigen Personen, so genannte Observer, verwaltet und diese automatisch über alle Zustandsänderungen benachrichtigt, normalerweise durch Aufrufen einer ihrer Methoden. Es wird hauptsächlich zur Implementierung von verteilten Ereignisbehandlungssystemen verwendet. Das Observer-Muster ist auch ein Schlüsselelement in dem bekannten MVC-Architekturmuster (Model-View-Controller). Wikipedia-Referenz

Grundsätzlich wird das Beobachtermuster verwendet, wenn Sie ein Objekt haben, das Beobachter über bestimmte Verhaltensweisen oder Zustandsänderungen informieren kann.

Zuerst können Sie eine globale Referenz (außerhalb einer Klasse) für das Benachrichtigungscenter erstellen:

let notifCentre = NotificationCenter.default

Toll jetzt können wir das von überall her nennen. Wir würden dann eine Klasse als Beobachter anmelden wollen ...

notifCentre.addObserver(self, selector: #selector(self.myFunc), name: "myNotification", object: nil)

Dadurch wird die Klasse als Beobachter für "readForMyFunc" hinzugefügt. Es zeigt auch an, dass die Funktion myFunc aufgerufen werden soll, wenn diese Benachrichtigung empfangen wird. Diese Funktion sollte in dieselbe Klasse geschrieben werden:

func myFunc(){
    print("The notification has been received")
}

Einer der Vorteile dieses Musters ist, dass Sie viele Klassen als Beobachter hinzufügen und somit nach einer Benachrichtigung viele Aktionen ausführen können.

Die Benachrichtigung kann jetzt einfach von fast überall im Code mit der folgenden Zeile gesendet (oder gesendet werden):

notifCentre.post(name: "myNotification", object: nil)

Sie können mit der Benachrichtigung auch Informationen als Wörterbuch übergeben

let myInfo = "pass this on"
notifCentre.post(name: "myNotification", object: ["moreInfo":myInfo])

Dann müssen Sie jedoch eine Benachrichtigung zu Ihrer Funktion hinzufügen:

func myFunc(_ notification: Notification){
    let userInfo = (notification as NSNotification).userInfo as! [String: AnyObject]
    let passedInfo = userInfo["moreInfo"]
    print("The notification \(moreInfo) has been received")
    //prints - The notification pass this on has been received
}

Kette der Verantwortung

In der objektorientierten Design ist die Zuständigkeitskette ein Designmuster einer Quelle , die aus command und eine Reihe von processing Jedes processing enthält eine Logik, die die Typen von Befehlsobjekten definiert, die es verarbeiten kann. Der Rest wird an das nächste processing in der Kette übergeben. Es gibt auch einen Mechanismus, um neue processing am Ende dieser Kette hinzuzufügen. Wikipedia

Einrichten der Klassen, aus denen die Verantwortungskette besteht.

Zunächst erstellen wir eine Schnittstelle für alle processing .

protocol PurchasePower {  
var allowable : Float { get }
  var role : String { get }
  var successor : PurchasePower? { get set }
}

extension PurchasePower {
  func process(request : PurchaseRequest){
    if request.amount < self.allowable {
      print(self.role + " will approve $ \(request.amount) for \(request.purpose)")
    } else if successor != nil {
      successor?.process(request: request)
    }
  }
}

Dann erstellen wir den command Objekt.

struct PurchaseRequest {
  var amount : Float
  var purpose : String
}

Erstellen Sie schließlich Objekte, die die Verantwortungskette ausmachen.

class ManagerPower : PurchasePower {
  var allowable: Float = 20
  var role : String = "Manager"
  var successor: PurchasePower?
}

class DirectorPower : PurchasePower {
  var allowable: Float = 100
  var role = "Director"
  var successor: PurchasePower?
}

class PresidentPower : PurchasePower {
  var allowable: Float = 5000
  var role = "President"
  var successor: PurchasePower?
}

Initiieren und ketten Sie es zusammen:

let manager = ManagerPower()
let director = DirectorPower()
let president = PresidentPower()

manager.successor = director
director.successor = president

Der Mechanismus zum Verketten von Objekten ist der Zugriff auf Eigenschaften

Anforderung zum Ausführen erstellen:

manager.process(request: PurchaseRequest(amount: 2, purpose: "buying a pen"))  // Manager will approve $ 2.0 for buying a pen
manager.process(request: PurchaseRequest(amount: 90, purpose: "buying a printer")) // Director will approve $ 90.0 for buying a printer

manager.process(request: PurchaseRequest(amount: 2000, purpose: "invest in stock")) // President will approve $ 2000.0 for invest in stock

Iterator

Bei der Computerprogrammierung ist ein Iterator ein Objekt, das es einem Programmierer ermöglicht, einen Container zu durchlaufen, insbesondere Listen. Wikipedia

struct Turtle {
  let name: String
}

struct Turtles {
  let turtles: [Turtle]
}

struct TurtlesIterator: IteratorProtocol {
  private var current = 0
  private let turtles: [Turtle]

  init(turtles: [Turtle]) {
    self.turtles = turtles
  }

  mutating func next() -> Turtle? {
    defer { current += 1 }
    return turtles.count > current ? turtles[current] : nil
  }
}

extension Turtles: Sequence {
  func makeIterator() -> TurtlesIterator {
    return TurtlesIterator(turtles: turtles)
  }
}

Und ein Anwendungsbeispiel wäre

let ninjaTurtles = Turtles(turtles: [Turtle(name: "Leo"),
                                     Turtle(name: "Mickey"),
                                     Turtle(name: "Raph"),
                                     Turtle(name: "Doney")])
print("Splinter and")
for turtle in ninjaTurtles {
  print("The great: \(turtle)")
}

Generator-Muster

Das Builder-Muster ist ein Entwurfsmuster für Objekterstellungssoftware . Im Gegensatz zu dem abstrakten Fabrikmuster und dem Fabrikmethode-Muster, das den Polymorphismus ermöglichen soll, soll das Builder-Muster eine Lösung für das Antimuster des teleskopierenden Konstruktors finden. Das Anti-Muster des Teleskopkonstruktors tritt auf, wenn die Kombination von Objektkonstruktorparametern zu einer exponentiellen Liste von Konstruktoren führt. Anstatt zahlreiche Konstruktoren zu verwenden, verwendet das Builder-Muster ein anderes Objekt, einen Builder, der jeden Initialisierungsparameter Schritt für Schritt empfängt und dann das resultierende konstruierte Objekt sofort zurückgibt.

-Wikipedia

Das Hauptziel des Builder-Patterns besteht darin, eine Standardkonfiguration für ein Objekt aus seiner Erstellung festzulegen. Es ist ein Vermittler zwischen dem Objekt und allen anderen Objekten, die mit dem Objekt verbunden sind.

Beispiel:

Um es klarer zu machen, werfen wir einen Blick auf ein Beispiel eines Auto-Builders .

Beachten Sie, dass wir über eine Autoklasse verfügen, die viele Optionen zum Erstellen eines Objekts enthält, z.

  • Farbe.
  • Anzahl der Sitze.
  • Anzahl der Räder
  • Art.
  • Gangart.
  • Motor.
  • Airbag-Verfügbarkeit
import UIKit

enum CarType {
    case
    
    sportage,
    saloon
}

enum GearType {
    case
    
    manual,
    automatic
}

struct Motor {
    var id: String
    var name: String
    var model: String
    var numberOfCylinders: UInt8
}

class Car: CustomStringConvertible {
    var color: UIColor
    var numberOfSeats: UInt8
    var numberOfWheels: UInt8
    var type: CarType
    var gearType: GearType
    var motor: Motor
    var shouldHasAirbags: Bool
    
    var description: String {
        return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\n Type: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)"
    }
    
    init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool) {
        
        self.color = color
        self.numberOfSeats = numberOfSeats
        self.numberOfWheels = numberOfWheels
        self.type = type
        self.gearType = gearType
        self.motor = motor
        self.shouldHasAirbags = shouldHasAirbags
        
    }
}

Ein Autoobjekt erstellen:

let aCar = Car(color: UIColor.black,
               numberOfSeats: 4,
               numberOfWheels: 4,
               type: .saloon,
               gearType: .automatic,
               motor: Motor(id: "101", name: "Super Motor",
                            model: "c4", numberOfCylinders: 6),
               shouldHasAirbags: true)

print(aCar)

/* Printing
 color: UIExtendedGrayColorSpace 0 1
 Number of seats: 4
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "101", name: "Super Motor", model: "c4", numberOfCylinders: 6)
 Airbag Availability: true
*/

Beim Erstellen eines Fahrzeugobjekts tritt das Problem auf , dass für das Fahrzeug viele Konfigurationsdaten erstellt werden müssen.

Für die Anwendung des Builder-Patterns sollten die Initialisierungsparameter Standardwerte haben, die bei Bedarf geändert werden können .

CarBuilder-Klasse:

class CarBuilder {
    var color: UIColor = UIColor.black
    var numberOfSeats: UInt8 = 5
    var numberOfWheels: UInt8 = 4
    var type: CarType = .saloon
    var gearType: GearType = .automatic
    var motor: Motor = Motor(id: "111", name: "Default Motor",
                             model: "T9", numberOfCylinders: 4)
    var shouldHasAirbags: Bool = false
    
    func buildCar() -> Car {
        return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags)
    }
}

Die CarBuilder Klasse definiert Eigenschaften, die geändert werden können, um die Werte des erstellten CarBuilder zu bearbeiten.

Bauen wir mit dem CarBuilder neue Autos auf:

var builder = CarBuilder()
// currently, the builder creates cars with default configuration.

let defaultCar = builder.buildCar()
//print(defaultCar.description)
/* prints
 color: UIExtendedGrayColorSpace 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: false
*/

builder.shouldHasAirbags = true
// now, the builder creates cars with default configuration,
// but with a small edit on making the airbags available

let safeCar = builder.buildCar()
print(safeCar.description)
/* prints
 color: UIExtendedGrayColorSpace 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: true
 */

builder.color = UIColor.purple
// now, the builder creates cars with default configuration
// with some extra features: the airbags are available and the color is purple

let femaleCar = builder.buildCar()
print(femaleCar)
/* prints
 color: UIExtendedSRGBColorSpace 0.5 0 0.5 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: true
*/

Der Vorteil der Anwendung des Builder-Patterns liegt in der einfachen Erstellung von Objekten, die viele Konfigurationen enthalten sollten, indem Standardwerte festgelegt werden. Außerdem können diese Standardwerte geändert werden.

Mach weiter:

Als bewährte Methode sollten alle Eigenschaften, für die Standardwerte erforderlich sind, in einem separaten Protokoll enthalten sein , das von der Klasse selbst und ihrem Builder implementiert werden sollte.

CarBluePrint unser Beispiel zu unterstützen, erstellen wir ein neues Protokoll mit dem Namen CarBluePrint :

import UIKit

enum CarType {
    case
    
    sportage,
    saloon
}

enum GearType {
    case
    
    manual,
    automatic
}

struct Motor {
    var id: String
    var name: String
    var model: String
    var numberOfCylinders: UInt8
}

protocol CarBluePrint {
    var color: UIColor { get set }
    var numberOfSeats: UInt8 { get set }
    var numberOfWheels: UInt8 { get set }
    var type: CarType { get set }
    var gearType: GearType { get set }
    var motor: Motor { get set }
    var shouldHasAirbags: Bool { get set }
}

class Car: CustomStringConvertible, CarBluePrint {
    var color: UIColor
    var numberOfSeats: UInt8
    var numberOfWheels: UInt8
    var type: CarType
    var gearType: GearType
    var motor: Motor
    var shouldHasAirbags: Bool
    
    var description: String {
        return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\n Type: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)"
    }
    
    init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool) {
        
        self.color = color
        self.numberOfSeats = numberOfSeats
        self.numberOfWheels = numberOfWheels
        self.type = type
        self.gearType = gearType
        self.motor = motor
        self.shouldHasAirbags = shouldHasAirbags
        
    }
}

class CarBuilder: CarBluePrint {
    var color: UIColor = UIColor.black
    var numberOfSeats: UInt8 = 5
    var numberOfWheels: UInt8 = 4
    var type: CarType = .saloon
    var gearType: GearType = .automatic
    var motor: Motor = Motor(id: "111", name: "Default Motor",
                             model: "T9", numberOfCylinders: 4)
    var shouldHasAirbags: Bool = false
    
    func buildCar() -> Car {
        return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags)
    }
}

Der Vorteil der Deklaration der Eigenschaften, die einen Standardwert in einem Protokoll benötigen, besteht darin, dass eine neue hinzugefügte Eigenschaft implementiert werden muss. Wenn eine Klasse einem Protokoll entspricht, muss sie alle ihre Eigenschaften / Methoden deklarieren.

Beachten Sie, dass es eine erforderliche neue Funktion gibt, die zum Bauplan eines Autos mit dem Namen "Batterie" hinzugefügt werden sollte:

protocol CarBluePrint {
    var color: UIColor { get set }
    var numberOfSeats: UInt8 { get set }
    var numberOfWheels: UInt8 { get set }
    var type: CarType { get set }
    var gearType: GearType { get set }
    var motor: Motor { get set }
    var shouldHasAirbags: Bool { get set }
    
    // adding the new property
    var batteryName: String { get set }
}

Beachten Sie nach dem Hinzufügen der neuen Eigenschaft, dass zwei Fehler bei der Kompilierung auftreten, die Sie darüber informieren, dass die Konformität mit dem CarBluePrint Protokoll die 'batteryName'-Eigenschaft erfordert. CarBuilder garantiert, dass CarBuilder einen Standardwert für die batteryName Eigenschaft batteryName .

Nach der Zugabe von batteryName neue Eigenschaft CarBluePrint Protokoll, die Umsetzung der beiden Car und CarBuilder Klassen sollte sein:

class Car: CustomStringConvertible, CarBluePrint {
    var color: UIColor
    var numberOfSeats: UInt8
    var numberOfWheels: UInt8
    var type: CarType
    var gearType: GearType
    var motor: Motor
    var shouldHasAirbags: Bool
    var batteryName: String
    
    var description: String {
        return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\nType: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)\nBattery Name: \(batteryName)"
    }
    
    init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool, batteryName: String) {
        
        self.color = color
        self.numberOfSeats = numberOfSeats
        self.numberOfWheels = numberOfWheels
        self.type = type
        self.gearType = gearType
        self.motor = motor
        self.shouldHasAirbags = shouldHasAirbags
        self.batteryName = batteryName
    }
}

class CarBuilder: CarBluePrint {
    var color: UIColor = UIColor.red
    var numberOfSeats: UInt8 = 5
    var numberOfWheels: UInt8 = 4
    var type: CarType = .saloon
    var gearType: GearType = .automatic
    var motor: Motor = Motor(id: "111", name: "Default Motor",
                             model: "T9", numberOfCylinders: 4)
    var shouldHasAirbags: Bool = false
    var batteryName: String = "Default Battery Name"
    
    func buildCar() -> Car {
        return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags, batteryName: batteryName)
    }
}

Lassen Sie uns wieder neue Autos mit dem CarBuilder :

var builder = CarBuilder()

let defaultCar = builder.buildCar()
print(defaultCar)
/* prints
 color: UIExtendedSRGBColorSpace 1 0 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: false
 Battery Name: Default Battery Name
*/

builder.batteryName = "New Battery Name"

let editedBatteryCar = builder.buildCar()
print(editedBatteryCar)
/*
 color: UIExtendedSRGBColorSpace 1 0 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: false
 Battery Name: New Battery Name
 */


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow