Schnelle Funktionen gegen berechnete Eigenschaften

26

Angenommen, ich habe eine Klasse Eventwie folgt:

class Event {
    private var attendees: [Person] = []

    // Case 1
    //*******
    // Should I use a func…
    func countOfAttendees() -> Int {
        return attendees.count
    }

    // …or a var
    var countOfAttendees: Int {
        return attendees.count
    }

    // Case 2
    //*******
    // Should I use a func…
    func countOfPaidAttendees() -> Int {
        return attendees.filter({$0.hasPaid}).count
    }

    // …or a var
    var countOfPaidAttendees: Int {
        return attendees.filter({$0.hasPaid}).count
    }
}

Ist es empfehlenswert, Funktionen oder berechnete Eigenschaften in den beiden oben angegebenen Fällen zu verwenden?

Ashley Mills
quelle
2
Kurz gesagt: "Lassen Sie Ihre Funktionen Funktionen und Ihre Eigenschaften Eigenschaften sein."
Robert Harvey

Antworten:

14

Befolgen Sie das Prinzip des einheitlichen Zugangs ,

Alle von einem Modul angebotenen Dienste sollten durch eine einheitliche Notation verfügbar sein, die nicht verrät, ob sie durch Speicherung oder durch Berechnung implementiert werden

Für mich bedeutet dies, dass ich keine Funktionen schreibe, die keine Argumente annehmen und einen Wert zurückgeben. Ich verwende immer berechnete Eigenschaften. Auf diese Weise kann ich, wenn ich mich später entscheide, die berechnete Eigenschaft in eine gespeicherte Eigenschaft umzuwandeln, ohne die Notwendigkeit haben zu müssen, die Parens überall in meiner App zu entfernen, und ohne eine separate "Getter" -Methode, die nur den Wert einer gespeicherten zurückgibt Eigentum, das meiner Meinung nach ziemlich verschwenderisch scheint.

Und wenn ich eine gespeicherte Eigenschaft in eine berechnete ändere, muss ich keine Parens am Ende hinzufügen, und zwar überall dort, wo sie in der App verwendet wird.

Daniel T.
quelle
Ursprünglich habe ich mich für die Komplexitätsantwort von @ Anton entschieden, aber in der Praxis wurde mir klar, dass ich das so mache ... Eigenschaften standardmäßig.
Ashley Mills
17

Ich würde sagen, das hängt von der Komplexität der Berechnung und der Nutzungshäufigkeit ab.

  • Wenn es sich um O(1)/ handelt *, verwenden Sie die berechnete Eigenschaft.
  • Wenn es O(N)+/ ist rare-use, dann benutze function.
  • Wenn es O(N)+/ ist frequent-use, überlegen Sie, ob Sie sich in Zukunft für die Verwendung von Caching oder anderen "intelligenten" Techniken entscheiden könnten, um die Komplexität zu kompensieren. Wenn "Ja", dann verwenden Sie Eigenschaft. Wenn "Nein-Nein-Nein, es ist nur schwer", dann verwenden Sie Funktion .
höflich
quelle
2
Komisch, wie ich angefangen habe, es mit der gleichen Begründung zu machen. Wenn Sie den Eindruck erwecken, eine Eigenschaft zu sein, machen Sie das Objekt zu einer Eigenschaft, auch wenn Sie eine leichte Verarbeitung durchführen müssen, solange es das Objekt nicht verändert.
Dielson Sales
8

Ich habe kürzlich angefangen, Kotlin zu lernen und sie haben eine großartige Heuristik darüber, wann sie berechnete Eigenschaften verwenden sollen:

Funktionen gegen Eigenschaften

In einigen Fällen können Funktionen ohne Argumente durch schreibgeschützte Eigenschaften ersetzt werden. Obwohl die Semantik ähnlich ist, gibt es einige stilistische Konventionen, wann man eine der anderen vorziehen soll.

Bevorzugen Sie eine Eigenschaft gegenüber einer Funktion, wenn der zugrunde liegende Algorithmus:

  • wirft nicht
  • hat eine O (1) -Komplexität
  • ist günstig zu kalkulieren
  • Gibt das gleiche Ergebnis über Aufrufe zurück

- https://kotlinlang.org/docs/reference/coding-conventions.html

Daniel T.
quelle
Das 'nicht werfen' ist auch wichtig für Swift, weil Eigenschaften (noch?) Nicht werfen können
Alejandromp
"hat eine O (1)
-Komplexität
7

In Swift haben Funktionen ohne Parameter und berechnete Eigenschaften fast die gleichen Funktionen (es kann einen Unterschied geben, dass eine Funktion ohne Parameter auch ein Abschluss ist, eine berechnete Eigenschaft jedoch nicht).

Der Unterschied ist semantisch. Wenn Ihr Code eine Aktion ausführt und beispielsweise eine Beschreibung des Ergebnisses dieser Aktion zurückgibt, würde ich eine Funktion verwenden. Wenn Ihr Code eine Eigenschaft berechnet, dies jedoch aus Sicht des Benutzers eine gespeicherte Eigenschaft sein könnte oder möglicherweise eine gespeicherte Eigenschaft, bei der zuerst ein zwischengespeicherter Wert aktualisiert werden muss, würde ich eine berechnete Eigenschaft verwenden.

Ein großer Unterschied: Was passiert, wenn Sie die Funktion oder die berechnete Eigenschaft zweimal aufrufen? Für eine berechnete Eigenschaft erwarte ich, dass x = Eigenschaft; y = Eigenschaft hat genau das gleiche Verhalten wie x = Eigenschaft; y = x, außer es läuft ein bisschen langsamer. Bei Funktionen wäre ich nicht überrascht, wenn das Verhalten anders wäre.

gnasher729
quelle
4

Verwenden Sie countOfAttendeesund countOfPaidAttendees().


Eine berechnete Variable gibt bei jedem Zugriff einen berechneten Wert zurück. Das heißt, es wird kein Wert gespeichert. Intern wird es als Funktion implementiert.

Was ist der Unterschied zu einer Funktion?

  • Semantisch ist eine Variable ein Zustand, eine Funktion eine Aktion.
  • Eine Funktion regelt den Zugriff auf den privaten Speicher. Eine berechnete Variable kann das gleiche auf kompaktere Weise tun. Beispiel .
  • Eine berechnete Variable kann mit KVO verwendet werden, als #keypath übergeben werden und hat die Möglichkeit, Folgendes zu beobachten: willSet, didSet.

Sie sollten eine Variable verwenden, wenn

  • es wirft nicht
  • es gibt eine einfache Eigenschaft zurück
  • es hat keine Nebenwirkung oder ein Verb im Namen
  • es ist O (1), das heißt, es entstehen keine signifikanten Kosten. In Ihrem Beispiel ist es O (n).
  • es ist idempotent. Mehrere identische Aufrufe geben den gleichen Wert zurück oder setzen das Objekt in den gleichen Zustand.

Irrelevante Gründe, eine Variable einer Funktion vorzuziehen

  • Eine berechnete Variable erspart Ihnen die Eingabe von (). Klarheit ist jedoch wichtiger als Kürze, daher ist dies ein schwaches Argument.
  • Eine Nur-Lese-Variable kann als Lese- / Schreibvariable überschrieben werden. Eine Funktion gibt an, dass sie immer schreibgeschützt ist. Apple verwendet jedoch Eigenschaften für schreibgeschützte Variablen wie array.count. Im Zweifelsfall Konsistenz mit der Plattform suchen.

Ressourcen

Ab  WWDC 2014 - 204 Was ist neu in Cocoa  ?> 24:40 Wann wird eine @ -Eigenschaft verwendet?

Verwenden Sie die Eigenschaft für alles, was sich auf den Wert oder den Zustand eines Objekts oder seine Beziehung zu anderen Objekten bezieht. Schlechte Kandidaten:

  • Methoden, die Dinge tun: Laden, Parsen, Umschalten,…. Sie haben Verben im Namen.
  • Generatoren: init, copy, enumerated,…. Diese Methoden sind nicht idempotent.
  • Methoden, die den Status ändern: nextObject.

Aus  Swift Style von Erica Sadun  > Berechnete Eigenschaften vs. Methoden

Eine Eigenschaft drückt die inhärente Qualität einer Instanz aus, während eine Methode eine Aktion ausführt.

  • Methoden haben Parameter; Eigenschaften nicht. Bevorzugen Sie Methoden für jeden Anruf mit Nebenwirkungen. Wenn eine Methode etwas ausführt (z. B. lädt, analysiert, wechselt oder druckt) oder einen Verbnamen hat, sollte sie keine Eigenschaft sein.
  • Bevorzugen Sie Eigenschaften für einfache Werte, die Sie abrufen und / oder festlegen können.
  • Eigenschaften sollten eine semantische intrinsische Qualität einer Typinstanz ausdrücken.
  • Mit Eigenschaften können Sie Beobachter über willSet und didSet hinzufügen. Im Gegensatz zu gespeicherten Instanzeigenschaften muss für gespeicherte Typeneigenschaften immer ein Standardwert angegeben werden.

Aus  Kotlin-Kodierungskonventionen> Funktionen vs. Eigenschaften . Siehe Daniels Antwort oben .

Andere Ressourcen ohne relevante Informationen:

Jano
quelle
3

Ich würde eine verwenden func. Objektorientierte Programmierung funktioniert einwandfrei ohne berechnete Eigenschaften. Da Sie einen Wert zurückerhalten, der berechnet / gefiltert wurde, argumentieren einige möglicherweise, dass sich eine berechnete Eigenschaft richtig anfühlt. Aber hier ist meine Beschwerde, wenn Sie das tun, dann wird die Lesbarkeit beeinträchtigt, weil sie sich wie ein Wert anfühlt.

In diesem Zusammenhang wäre es nicht sinnvoll, den berechneten Wert zuzuweisen (und zum Glück hilft uns die IDE dabei, dies zu vermeiden). Was passiert jedoch, wenn ich versuche, etwas zuzuweisen, das berechnet wurde, aber wie ein Wert aussieht?

event.countOfAttendees = 0; // not possible

Während der Verwendung von func weiß der Anrufer, dass Sie sich nicht direkt mit einem Wert befassen:

event.countOfAttendees()

Ich denke, wenn es sich um ein Verhaltensobjekt handelt, sollte es so aussehen, als würde es sich verhalten, anstatt wie eine Datenstruktur auszusehen. Wenn Ihr Objekt dumm ist und kein Verhalten aufweist, warum sollten Sie dann versuchen, es einzukapseln? In diesem Fall können die Teilnehmer auch öffentlich sein

Morbidhawk
quelle