2017-01-22 3 views
1

Können wir zwischen Klassen wie unten Code kommunizieren, anstatt Delegate/Protokoll Muster und Benachrichtigung zu verwenden. Ich habe kein Codebeispiel wie dieses gesehen. Aber nur neugierig zu wissen, warum das funktionieren sollte oder nicht.Schwache Referenz zwischen Eltern- und Kindklassen

class Class1 { 

    var class2Obj: Class2 = Class2() 
    init() { 
     class2Obj.class1Obj = self 
    } 

    func class1Method() { 
     print("Parent") 
    } 
} 

class Class2 { 

    weak var class1Obj: Class1? 

    func class2Method() { 
     class1Obj.class1Method() 
    } 

} 
+0

Ja, Sie können so kommunizieren.Aber der Delegierte, der Block und die Schließung sind verständlicher für andere Leute zu verstehen, dass Class2 Aufrufe über die "Methode" zurückrufen. Es ist ein "Was ist die Best Practice" -Ausgabe :) –

+0

@YUNCHEN - Ja, Sie können auch das Verschlussmuster verwenden. Aber ich würde die Behauptung qualifizieren, dass es "best practice" ist, Verschlüsse zu verwenden. Wie die meisten Design-Optionen Es ist komplizierter als das. Es kommt darauf an. In einfachen Fällen kann eine Schließung nützlich sein. Aber in reicheren Schnittstellen ist das Delegierten-Protokoll-Muster oft überlegen. Es hängt nur von der Art der Schnittstelle ab. Aber ich stimme zu, Anshus Muster aus zwei eng gekoppelten Klassen ist nicht optimal: – Rob

+0

@Rob, stimme dir zu. –

Antwort

3

Was haben Sie hier ist ein Delegierter Muster. Ihre Delegateigenschaft wird lediglich class1Obj anstelle des üblicheren Namens delegate genannt. Das Hauptproblem hier ist, dass Sie kein Protokoll verwenden, und als Ergebnis sind diese zwei Klassen "eng gekoppelt", d. H. Class2 hängt stark von Einzelheiten der Implementierung ab. Außerdem ist bei diesen beiden eng gekoppelten Klassen nicht sofort klar, welche Methoden von Class1Class2 benötigen könnten. Es macht die Wartung von Class1 schwieriger, weil es so leicht ist, versehentlich eine Änderung vorzunehmen, die das Verhalten in Class2 unterbricht. Es macht es auch schwierig, Class2 in Verbindung mit einer anderen Klasse als Class1 zu verwenden.

Stattdessen deklarieren Sie im Allgemeinen ein Protokoll, um die Art des Vertrags genau zwischen Class2 und einem anderen Objekt zu artikulieren, das sie möglicherweise verwenden muss. Das Ergebnis ist, dass die Klassen weniger eng gekoppelt sind, d. H. Class2 muss nichts über die andere Klasse wissen als ihre Übereinstimmung mit dem fraglichen Protokoll. Wenn Sie bei der Bearbeitung Class1 deklarieren, dass es dem Protokoll entspricht, wird der Compiler Sie warnen, wenn Sie eine erforderliche Methode oder Eigenschaft nicht implementieren können.

Also, mit einer vernachlässigbaren Menge an Arbeit im Voraus, macht das Protokoll den Code viel einfacher zu pflegen. Es bietet auch einige zusätzliche Flexibilität, wobei Sie Class2 in Verbindung mit etwas anderem als Class1 in der Zukunft verwenden können.

Unterm Strich können Protokolle zu Code führen, der leichter zu warten ist und flexibler ist, ohne versteckte Annahmen.


Wenn Sie nicht wollen, den Delegaten-Protokoll Muster verwenden, ist eine weitere Alternative, einen Verschluß zu verwenden, wo Class1 einen Codeblock liefert, die Class2 aufrufen können. So können Sie so etwas wie:

class Class1 { 

    var class2Obj = Class2() 

    init() { 
     class2Obj.handler = { [weak self] in  // note `weak` reference which avoids strong reference cycle 
      self?.class1Method() 
     } 
    } 

    func class1Method() { 
     print("Parent") 
    } 
} 

class Class2 { 

    var handler: (() -> Void)? 

    func class2Method() { 
     handler?() 
    } 

} 

Während die Delegierten-Protokoll Muster nützlich ist, wenn Sie eine reiche Schnittstelle zwischen den beiden Klassen, dieses Verschlussmuster, wenn Sie eine sehr einfache Schnittstelle zwischen den beiden Klassen haben.

Ehrlich gesagt, eine häufigere Permutation der oben genannten ist, wo die Schließung direkter mit einer bestimmten Anfrage verbunden ist, die Class1 in Class2 initiiert. Sie können also den Parameter in Class2 als Abschluss für die entsprechende Methode definieren. Darüber hinaus sind vorbei Sie oft Daten zurück, so vorstellen, dass wir vorbei wieder eine optionales String:

class Class1 { 

    var class2Obj = Class2() 

    func performClass2Method() { 
     class2Obj.class2Method { string in 
      guard let string = string else { return } 

      self.class1Method() 
     } 
    } 

    func class1Method() { 
     print("Parent") 
    } 
} 

class Class2 { 

    func class2Method(completionHandler: @escaping (String?) -> Void) { 
     // do something which creates `string` 

     // when done, call the closure, passing that `string` value back 

     completionHandler(string) 
    } 

} 

Diese Verschlussmuster eine gute Möglichkeit, einfache Schnittstellen zwischen Objekten zu tun, sondern halten auch die beiden Klassen sehr lose gekoppelt (dh Class2 hat keine Abhängigkeiten von Class1), ähnlich wie die Verwendung eines Protokolls im Delegiertenmuster. Aber hoffentlich zeigen die obigen Abschlussbeispiele eine einfache Alternative zum reichen Delegierten-Protokoll-Muster.

Verwandte Themen