Was ist die beste Vorgehensweise zum Benennen von Swift-Dateien, die vorhandenen Objekten Erweiterungen hinzufügen?

165

Es ist möglich, Erweiterungen zu vorhandenen Swift-Objekttypen mithilfe von Erweiterungen hinzuzufügen, wie in der Sprachspezifikation beschrieben .

Infolgedessen können Erweiterungen wie die folgenden erstellt werden:

extension String {
    var utf8data:NSData {
        return self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    }
}

Was ist jedoch die beste Benennungspraxis für Swift-Quelldateien, die solche Erweiterungen enthalten?

In der Vergangenheit sollte die Konvention extendedtype+categoryname.mfür den Objective-C-Typ verwendet werden, wie im Objective-C-Handbuch erläutert . Das Swift-Beispiel hat jedoch keinen Kategorienamen, und es String.swifterscheint nicht angemessen , ihn aufzurufen .

Die Frage ist also: StringWie soll die schnelle Quelldatei angesichts der obigen Erweiterung heißen?

AlBlue
quelle
4
Dies ist keine Codereview-Frage. Dieses spezielle Beispiel interessiert mich nicht. Ich möchte wissen, wie die schnelle Namenskonvention lautet.
AlBlue
2
Es gibt keine Namenskonvention. Das einzige, was wir tun müssen, sind Kategorien aus Objective-C, die immer dem ClassName+ExtensionNameFormat folgten und die ich nicht zu viele Leute sehe, die sie noch verwenden. Außerdem finde ich das klobig, anstatt nur Klassen und Erweiterungen zusammen zu definieren oder der Datei einen besseren Namen zu geben FooAbleTypesund Instanzen insgesamt zu definieren.
CodaFi
4
Es gibt noch keine Benennungspraxis. Hier ist ein Gedanke: Fassen Sie alle globalen Erweiterungen in einer einzigen zusammen Extensions.swift. Auf diese Weise verlieren Sie nicht den Überblick und Neulinge in der Codebasis werden sie sofort bemerken. Und ich würde es vorziehen, einmalige Erweiterungen für die Datei, in der sie benötigt werden, privat zu halten.
Andrew
1
Wie Andrew sagt, gibt es noch keine Standard-Benennungspraxis - daher wurde diese Frage gestellt, um gezielt Meinungen einzuholen, damit eine neu gebildete Community zu einigen vorgeschlagenen Ideen kommen kann.
AlBlue
1
Eine einzige Datei extensions.swift ist meiner Meinung nach der richtige Weg. Halten Sie die Struktur darin (auf Ihre eigene Weise) organisiert, um leicht zu finden, was Sie brauchen. Eine einzelne Datei kann einfach aus einer Vielzahl von Projekten kopiert oder verlinkt werden, ohne dass etwas vergessen wird.
Yohst

Antworten:

202

Die meisten Beispiele, die ich gesehen habe, ahmen den Objective-C-Ansatz nach. Die obige Beispielerweiterung wäre:

String+UTF8Data.swift

Die Vorteile sind, dass die Namenskonvention leicht verständlich macht, dass es sich um eine Erweiterung handelt und welche Klasse erweitert wird.

Das Problem bei der Verwendung Extensions.swiftoder sogar StringExtensions.swiftist, dass es nicht möglich ist, den Zweck der Datei anhand ihres Namens abzuleiten, ohne auf deren Inhalt zu achten.

Die Verwendung xxxable.swiftdes von Java verwendeten Ansatzes funktioniert in Ordnung für Protokolle oder Erweiterungen, die nur Methoden definieren. Im obigen Beispiel wird jedoch ein Attribut definiert, UTF8Dataable.swiftdas grammatikalisch wenig sinnvoll ist.

Picciano
quelle
1
Obwohl man schließen kann, was durch die Namenskonvention erweitert wird, ist IHMO eine unnötige Komplikation. Anstelle von Tonnen von <Name> + <Erweiterung> .swift-Dateien behalte ich eine einzelne extensions.swift-Datei, die ich normalerweise für jedes Projekt verwende. Die Datei ist intern so organisiert, dass es einfach ist, eine bestimmte erweiterte Klasse zu finden.
Yohst
18
Diese Antwort, <Name> + <Erweiterung> .swift, entspricht in der Tat der Vorgehensweise von Xcode beim Erstellen von NSManagedObject-Unterklassen für Kerndaten in Xcode 8. Beispiel: Foo + CoreDataProperties.swift.
Jerry Krinock
4
Was ist, wenn die Erweiterung mehrere Methoden implementiert?
AlexVPerl
2
Sei einfach so beschreibend wie möglich. Wenn Sie beispielsweise eine Erweiterung für Image haben, die verschiedene Funktionen zum Anwenden von Filtern enthält, nennen Sie sie Image + Filters.swift. Es ist in Ordnung, verschiedene Dateien für verwandte Gruppen für erweiterte Funktionen zu verwenden. Gruppieren Sie verwandte Dinge, aber halten Sie nicht verwandte Dinge getrennt. Das Leben wird gut sein.
Picciano
Wenn Sie die Konvention von verwenden ExtendedType+Functionality.swift, empfiehlt es sich, alle StringErweiterungen beispielsweise in ihren eigenen Unterordner (dh Stringoder String Extensions) unter dem ExtensionsOrdner zu sortieren ? Oder ist es besser, alle Erweiterungsdateien auf derselben Ebene unter dem ExtensionsOrdner zu speichern ?
Noah Wilder
8

Es gibt keine Swift-Konvention. Halte es einfach:

StringExtensions.swift

Ich erstelle eine Datei für jede Klasse, die ich erweitere. Wenn Sie für alle Erweiterungen eine einzige Datei verwenden, wird diese schnell zu einem Dschungel.

Mike Taverne
quelle
8
Dies scheint nicht besonders wiederverwendbar zu sein.
Keller
1
Verglichen mit?
Mike Taverne
3
Im Vergleich zu einer einzeln (oder eng gekoppelten) Datei mit Klassenerweiterungen, die einem einzigen (oder ausdrücklich zuordenbaren) Zweck dienen. So etwas wie "StringExtensions" klingt so, als könnte es alles enthalten, von der allgemeinen String-Bereinigung bis hin zur app-spezifischen Logik. Dies ist möglicherweise nicht der beste Ansatz, wenn die Wiederverwendung ein Problem darstellt. Die Namenskonvention für Kakao tendiert eher zur Funktion als zur Implementierung. Ich würde argumentieren, dass "StringExtensions" letzteres anzeigt. Abgesehen von der Namenskonvention bevorzuge ich die akzeptierte Antwort, sicherlich in ObjC, aber in Swift scheint es aufgrund der Module ein noch besserer Ansatz zu sein.
Keller
2
Das macht Sinn. Ich dachte eher an eine einzelne App, bei der die Wiederverwendung kein Problem darstellt. Angenommen, ich habe einige nicht verwandte Zeichenfolgenfunktionen, die ich als Erweiterungen verwenden möchte. Ich könnte eine Datei erstellen und alle diese Funktionen dort ablegen oder eine Datei pro Funktion erstellen. Ich mag die Einfachheit einer einzelnen Datei in diesem Fall. Aber Ihre Argumentation ist vernünftig. Vielen Dank.
Mike Taverne
Dies ist absolut sinnvoll, vorausgesetzt, die hier hinzugefügten Elemente gelten natürlich für alle Zeichenfolgen (z. B. 'trimRight ()' als Beispiel). Wenn es sich um etwas handelt, das eher für den Anwendungsfall spezifisch ist (z. B. 'formatAccountNumber ()'), sollte die Datei 'Strings + AccountFormatting.swift' sein und nur dort verwendet werden, wo sie tatsächlich verwendet wird, um das nicht zu überladen Oberflächen-API "Strings" an anderer Stelle.
Mark A. Donohoe
1

Ich bevorzuge es, StringExtensions.swiftbis ich zu viele Dinge hinzugefügt habe, um die Datei in etwas wie String+utf8Data.swiftund aufzuteilen String+Encrypt.swift.

Wenn Sie ähnliche Dateien zu einer kombinieren, wird Ihr Gebäude schneller. Siehe Optimieren von Swift-Build-Zeiten

DawnSong
quelle
1
Das hat zwei Dateinamenskonventionen für dasselbe. Ich finde das schlecht.
Sinn-Angelegenheiten
@ Bedeutung-Angelegenheiten Es kommt darauf an. Die beiden Namenskonventionen sind bekannt und werden von Apple Documents empfohlen. Mach wie du es willst.
DawnSong
Ich wünschte, mehr Programmierer würden nach Eleganz streben, indem sie Namens- und Codevariationen einschränken.
Sinn-Angelegenheiten
@ Bedeutungsangelegenheiten Eleganz hat zwei Seiten, es ist wie ein klassisches kontroverses Problem, wie man geschweifte Klammern in C-ähnlichen Sprachen schreibt. Es ist trivial, daher denke ich nicht, dass es notwendig ist, eines auszuwählen und es obligatorisch zu machen, bis die meisten Leute damit einverstanden sind.
DawnSong
Ich meinte die Eleganz der Konsistenz: Verwenden Sie eine Möglichkeit, Erweiterungen zu benennen, oder eine Möglichkeit, geschweifte Klammern zu platzieren. Dann denke ich, dass es einen messbaren Unterschied in der Lesbarkeit verschiedener geschweifter Klammerstile gibt; Ich denke also nicht, dass es überhaupt "trivial" ist.
Sinn-Angelegenheiten
0

Wenn Sie über eine vom Team vereinbarte Reihe allgemeiner und verschiedener Verbesserungen verfügen, funktioniert das Zusammenfassen als Extensions.swift als Keep-It-Simple-Lösung der ersten Ebene. Wenn Ihre Komplexität jedoch zunimmt oder die Erweiterungen komplexer werden, ist eine Hierarchie erforderlich, um die Komplexität zu kapseln. Unter solchen Umständen empfehle ich die folgende Übung mit einem Beispiel.

Ich hatte eine Klasse, die mit meinem Backend spricht Server. Es wurde immer größer und deckte zwei verschiedene Ziel-Apps ab. Einige Leute mögen eine große Datei, teilen sich aber logischerweise mit Erweiterungen auf. Ich bevorzuge es, jede Datei relativ kurz zu halten, deshalb habe ich die folgende Lösung gewählt. Serverursprünglich CloudAdapterProtocolalle seine Methoden angepasst und implementiert. Was ich getan habe, war, das Protokoll in eine Hierarchie umzuwandeln, indem ich es auf untergeordnete Protokolle verweisen ließ:

protocol CloudAdapterProtocol: ReggyCloudProtocol, ProReggyCloudProtocol {
    var server: CloudServer {
        get set
    }
    func getServerApiVersion(handler: @escaping (String?, Error?) -> Swift.Void)
}

In Server.swifthabe ich

import Foundation
import UIKit
import Alamofire
import AlamofireImage

class Server: CloudAdapterProtocol {
.
.
func getServerApiVersion(handler: @escaping (String?, Error?) -> Swift.Void) {
.
.
}

Server.swiftImplementiert dann einfach die Kernserver-API, um den Server festzulegen und die API-Version abzurufen. Die eigentliche Arbeit ist in zwei Dateien aufgeteilt:

Server_ReggyCloudProtocol.swift
Server_ProReggyCloudProtocol.swift

Diese implementieren die jeweiligen Protokolle.

Es bedeutet, dass Sie Importdeklarationen in den anderen Dateien haben müssen (in diesem Beispiel für Alamofire), aber meiner Ansicht nach ist dies eine saubere Lösung hinsichtlich der Trennung von Schnittstellen.

Ich denke, dieser Ansatz funktioniert genauso gut mit extern festgelegten Klassen wie mit Ihren eigenen.

Faisal Memon
quelle
0

Warum ist das überhaupt eine Debatte? Soll ich alle meine Unterklassen in eine Datei namens _Subclasses.swift einfügen? Ich denke nicht. Swift hat einen modulbasierten Namensabstand. Um eine bekannte Swift-Klasse zu erweitern, ist eine Datei erforderlich, die für ihren Zweck spezifisch ist. Ich könnte ein großes Team haben, das eine Datei namens UIViewExtensions.swift erstellt, die keinen Zweck ausdrückt und Entwickler verwirrt und leicht in dem Projekt dupliziert werden kann, das nicht erstellt werden würde. Die Objective-C-Namenskonvention funktioniert einwandfrei. Bis Swift einen echten Namensabstand aufweist, ist dies der beste Weg.

Tom Condon
quelle
In meinem Fall halte ich es für absolut sinnvoll, eine Datei mit dem Namen UIViewExtensions.swift zu haben, vorausgesetzt, die in dieser Datei definierten Erweiterungen sind für alle UIView-Klassen sinnvoll, z. B. für eine Methode 'placeIn (UIView)'. Wenn es nutzungsspezifisch ist (dh nur für einen Teil der App, z. B. für die Dekoration von benutzerdefinierten Ansichten, dann würde ich UIView + CustomDecoration.swift ausführen. Der Punkt ist, dass Sie die Verwendung in Betracht ziehen müssen, bevor Sie eine Verallgemeinerung wie das Aussprechen einer Datei mit dem Namen 'UIViewExtensions' vornehmen .swift, die keinen Zweck ausdrücken ', wenn der Zweck allgemeine Erweiterungen für alle UIViews sind.
Mark A. Donohoe
0

Anstatt meine Kommentare überall hinzuzufügen, tauche ich sie alle hier in einer Antwort auf.

Persönlich verfolge ich einen hybriden Ansatz, der sowohl eine gute Benutzerfreundlichkeit als auch Klarheit bietet und gleichzeitig die API-Oberfläche für das Objekt, das ich erweitere, nicht überfüllt.

Zum Beispiel würde alles, was Sinn macht , für eine beliebige Zeichenfolge verfügbar zu sein , StringExtensions.swiftwie trimRight()und removeBlankLines().

Wenn ich jedoch eine Erweiterungsfunktion hätte, wie formatAsAccountNumber()sie nicht in diese Datei aufgenommen würde, da 'Kontonummer' natürlich nicht für alle Zeichenfolgen gilt und nur im Zusammenhang mit Konten sinnvoll ist. In diesem Fall würde ich eine Datei mit dem Namen Strings+AccountFormatting.swiftoder vielleicht sogar Strings+CustomFormatting.swiftmit einer formatAsAccountNumber()Funktion erstellen, wenn es mehrere Arten / Möglichkeiten gibt, sie tatsächlich zu formatieren.

Tatsächlich habe ich in diesem letzten Beispiel mein Team aktiv davon abgehalten, solche Erweiterungen zu verwenden, und würde stattdessen so etwas AccountNumberFormatter.format(String)empfehlen, da dies die StringAPI-Oberfläche überhaupt nicht berührt , wie es nicht sein sollte. Die Ausnahme wäre, wenn Sie diese Erweiterung in derselben Datei definieren würden, in der sie verwendet wird, aber dann hätte sie sowieso keinen eigenen Dateinamen.

Mark A. Donohoe
quelle
0

Ich bevorzuge es +, die Tatsache zu unterstreichen, dass es Erweiterungen enthält:

String+Extensions.swift

Und wenn die Datei zu groß wird, können Sie sie für jeden Zweck aufteilen:

String+UTF8Data.swift

String+Encrypt.swift

Xys
quelle