Jaka jest najlepsza metoda nazywania plików Swift, które dodają rozszerzenia do istniejących obiektów?

165

Możliwe jest dodawanie rozszerzeń do istniejących typów obiektów Swift za pomocą rozszerzeń, zgodnie z opisem w specyfikacji języka .

Dzięki temu możliwe jest tworzenie rozszerzeń takich jak:

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

Jaka jest jednak najlepsza praktyka nazywania plików źródłowych Swift zawierających takie rozszerzenia?

W przeszłości konwencja była używana extendedtype+categoryname.mdla typu Objective-C, jak omówiono w przewodniku Objective-C . Ale przykład Swift nie ma nazwy kategorii, a nazywanie jej String.swiftnie wydaje się odpowiednie.

Tak więc pytanie brzmi: biorąc pod uwagę powyższe Stringrozszerzenie, jak powinien się nazywać szybki plik źródłowy?

AlBlue
źródło
4
To nie jest pytanie dotyczące przeglądu kodu - nie obchodzi mnie ten konkretny przykład, chcę wiedzieć, jaka jest konwencja szybkiego nazewnictwa.
AlBlue
2
Nie ma konwencji nazewnictwa. Jedyne, co musimy zrobić, to kategorie z Objective-C, które zawsze były zgodne z ClassName+ExtensionNameformatem i których nie widzę zbyt wielu ludzi nadal używa. Poza tym uważam, że jest to niezgrabne zamiast po prostu definiowania klas i rozszerzeń razem lub nadawania plikowi lepszej nazwy FooAbleTypesi definiowania instancji w agregacji.
CodaFi,
4
Nie ma jeszcze żadnego praktyka nazewnictwa. Oto myśl: zbierz wszystkie globalne rozszerzenia w jednym Extensions.swift. W ten sposób nie stracisz ich z oczu, a nowicjusze w bazie kodu natychmiast je zauważą. I wolałbym, aby jednorazowe rozszerzenia były prywatne w stosunku do pliku, w którym są potrzebne.
Andrew
1
Jak mówi Andrew, nie ma jeszcze standardowej praktyki nazewnictwa - stąd to pytanie zostało zadane, aby uzyskać konkretne opinie, aby nowo utworzona społeczność mogła dojść do niektórych sugerowanych pomysłów.
AlBlue
1
Moim zdaniem pojedynczy plik extensions.swift jest drogą. Utrzymuj strukturę w nim zorganizowaną (na swój własny sposób), aby łatwo znaleźć to, czego potrzebujesz. Pojedynczy plik można łatwo skopiować lub połączyć z różnych projektów i nie zapomnieć o wielu rzeczach.
Yohst

Odpowiedzi:

202

Większość przykładów, które widziałem, naśladuje podejście Objective-C. Przykładowe rozszerzenie powyżej to:

String+UTF8Data.swift

Zaletą jest to, że konwencja nazewnictwa ułatwia zrozumienie, że jest to rozszerzenie i która klasa jest rozszerzana.

Problem z używaniem, Extensions.swifta nawet StringExtensions.swiftpolega na tym, że nie można wywnioskować przeznaczenia pliku na podstawie jego nazwy bez patrzenia na jego zawartość.

Używanie xxxable.swiftpodejścia używanego przez Javę działa dobrze w przypadku protokołów lub rozszerzeń, które definiują tylko metody. Ale znowu, powyższy przykład definiuje atrybut, więc UTF8Dataable.swiftnie ma to większego sensu gramatycznego.

picciano
źródło
1
Chociaż można wywnioskować, co jest rozszerzane przez konwencję nazewnictwa, jest to IHMO niepotrzebną komplikacją. Zamiast ton plików <nazwa> + <rozszerzenie> .swift, trzymam pojedynczy plik extensions.swift, którego zwykle używam do każdego projektu. Plik jest wewnętrznie zorganizowany w taki sposób, że znalezienie konkretnej rozszerzonej klasy jest łatwe.
Yohst
18
Ta odpowiedź, <nazwa> + <extention> .swift, jest rzeczywiście tym, w jaki Xcode robi to podczas tworzenia podklas NSManagedObject dla danych podstawowych w Xcode 8. Przykład: Foo + CoreDataProperties.swift.
Jerry Krinock,
4
Co się stanie, jeśli rozszerzenie implementuje wiele metod?
AlexVPerl
2
Po prostu bądź jak najbardziej opisowy. Na przykład, jeśli masz rozszerzenie Image, które zawiera inną funkcję do stosowania filtrów, nazwij je Image + Filters.swift. W przypadku funkcji rozszerzonych można używać różnych plików dla powiązanych grup. Grupuj powiązane rzeczy, ale oddzielaj niepowiązane rzeczy. Życie będzie dobre.
picciano
Jeśli korzystasz z konwencji ExtendedType+Functionality.swift, czy dobrym zwyczajem jest sortowanie wszystkich Stringrozszerzeń, na przykład, do ich własnego podfolderu (tj. StringLub String Extensions) w Extensionsfolderze? A może lepiej jest po prostu przechowywać wszystkie pliki rozszerzeń na tym samym poziomie w Extensionsfolderze?
Noah Wilder
8

Nie ma konwencji Swift. Nie komplikuj:

StringExtensions.swift

Tworzę jeden plik dla każdej rozszerzanej klasy. Jeśli użyjesz jednego pliku dla wszystkich rozszerzeń, szybko stanie się on dżunglą.

Mike Taverne
źródło
8
To nie wydaje się szczególnie nadające się do ponownego użycia.
Keller
1
W porównaniu do?
Mike Taverne
3
W porównaniu z pojedynczym (lub ściśle powiązanym) plikiem rozszerzeń klas, które służą jednemu (lub bezpośrednio powiązanemu) celowi. Coś w rodzaju „StringExtensions” brzmi tak, jakby mogło zawierać wszystko, od ogólnego oczyszczania ciągów po logikę specyficzną dla aplikacji - co może nie być najlepszym podejściem, jeśli ponowne użycie jest problemem. Konwencja nazewnictwa kakao skłania się raczej w kierunku funkcji niż implementacji. Twierdzę, że „StringExtensions” wskazuje na to drugie. Pomijając konwencję nazewnictwa, wolę zaakceptowaną odpowiedź, na pewno w ObjC, ale w Swift wydaje się to jeszcze lepszym podejściem ze względu na moduły.
Keller
2
To ma sens. Myślałem bardziej o pojedynczej aplikacji, w której ponowne użycie nie było problemem. Na przykład, powiedzmy, że mam kilka niezwiązanych ze sobą funkcji tekstowych, których chcę używać jako rozszerzeń - mógłbym utworzyć jeden plik i umieścić tam wszystkie te funkcje lub utworzyć jeden plik na funkcję. W takim przypadku podoba mi się prostota pojedynczego pliku. Ale twoje rozumowanie jest rozsądne. Dzięki.
Mike Taverne
Ma to całkowity sens, pod warunkiem, że dodawane tutaj rzeczy będą naturalnie miały zastosowanie do wszystkich łańcuchów (np. „TrimRight ()” jako przykład). Jeśli jest to coś, co jest bardziej specyficzne dla przypadku użycia (np. „FormatAccountNumber ()”), to plik powinien mieć nazwę „Strings + AccountFormatting.swift” i powinien mieć zasięg tylko tam, gdzie jest faktycznie używany, aby nie zaśmiecać 'Strings' powierzchniowy interfejs API w innym miejscu.
Mark A. Donohoe
1

Wolę, StringExtensions.swiftdopóki nie dodam zbyt wielu rzeczy, aby podzielić plik na coś takiego jak String+utf8Data.swifti String+Encrypt.swift.

Jeszcze jedno, połączenie podobnych plików w jeden przyspieszy budowanie. Zobacz Optymalizacja-Swift-Build-Times

DawnSong
źródło
1
To ma dwie konwencje nazewnictwa plików dla tego samego. Myślę, że to źle.
znaczenie ma znaczenie
@ znaczenie-ma znaczenie To zależy. Obie konwencje nazewnictwa są dobrze znane i zalecane przez Apple Documents. Zrób jak uważasz.
DawnSong
Chciałbym, żeby więcej programistów dążyło do elegancji, ograniczając nazewnictwo i odmiany [formatowania] kodu.
znaczenie ma znaczenie
@ Znaczenie-ma znaczenie Elegancja ma dwie strony, to jak klasyczny kontrowersyjny problem dotyczący pisania nawiasów klamrowych w językach C-podobnych. Jest to trywialne, więc nie sądzę, aby trzeba było wybrać jeden i uczynić go obowiązkowym, dopóki większość ludzi się na to nie zgodzi.
DawnSong
Miałem na myśli elegancję spójności: jeden sposób na rozszerzenie nazw lub jeden sposób umieszczenia nawiasów klamrowych. Wtedy myślę, że istnieje wymierna różnica w czytelności różnych stylów nawiasów klamrowych; więc nie sądzę, żeby to było „trywialne”.
znaczenie ma znaczenie
0

Jeśli masz uzgodniony przez zespół zestaw wspólnych i różnych ulepszeń, wrzucenie ich razem jako Extensions.swift działa jako rozwiązanie pierwszego poziomu Keep-It-Simple. Jednak w miarę wzrostu złożoności lub większego zaangażowania rozszerzeń potrzebna jest hierarchia, aby zamknąć złożoność. W takich okolicznościach polecam następującą praktykę wraz z przykładem.

Miałem klasę, która rozmawiała z moim zapleczem, o nazwie Server. Zaczął się rozrastać, aby objąć dwie różne aplikacje docelowe. Niektórzy ludzie lubią duży plik, ale logicznie dzielą się na rozszerzenia. Wolę, aby każdy plik był stosunkowo krótki, więc wybrałem następujące rozwiązanie. Serverpierwotnie dostosował się CloudAdapterProtocoli wdrożył wszystkie jej metody. To, co zrobiłem, to przekształcić protokół w hierarchię, czyniąc z niego odniesienie do protokołów podrzędnych:

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

W Server.swiftmam

import Foundation
import UIKit
import Alamofire
import AlamofireImage

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

Server.swiftnastępnie po prostu implementuje podstawowe API serwera do ustawiania serwera i pobierania wersji API. Prawdziwa praca jest podzielona na dwa pliki:

Server_ReggyCloudProtocol.swift
Server_ProReggyCloudProtocol.swift

Implementują one odpowiednie protokoły.

Oznacza to, że musisz mieć deklaracje importu w innych plikach (dla Alamofire w tym przykładzie), ale moim zdaniem jest to czyste rozwiązanie pod względem segregowania interfejsów.

Myślę, że to podejście działa równie dobrze w przypadku klas określonych zewnętrznie, jak i własnych.

Faisal Memon
źródło
0

Dlaczego to w ogóle debata? Czy powinienem umieścić wszystkie moje podklasy w pliku o nazwie _Subclasses.swift. Myślę, że nie. Swift ma odstępy między nazwami oparte na modułach. Aby rozszerzyć dobrze znaną klasę Swift, potrzebny jest plik odpowiedni do jej przeznaczenia. Mógłbym mieć duży zespół, który utworzyłby plik UIViewExtensions.swift, który nie ma żadnego celu i wprowadziłby w błąd programistów i mógłby być łatwo zduplikowany w projekcie, który nie zostałby zbudowany. Konwencja nazewnictwa Objective-C działa dobrze i dopóki Swift nie ma prawdziwych odstępów między nazwami, jest to najlepsza droga.

Tom Condon
źródło
W moim przypadku myślę, że posiadanie pliku o nazwie UIViewExtensions.swift ma sens, pod warunkiem, że rozszerzenia zdefiniowane w tym pliku miałyby sens dla każdej / wszystkich klas UIView, takich jak metoda „placeIn (UIView)”. Jeśli jest to specyficzne dla zastosowania (np. Tylko dla części aplikacji, powiedzmy wokół niestandardowej dekoracji widoku, zrobię UIView + CustomDecoration.swift. Chodzi o to, że musisz rozważyć użycie przed dokonaniem uogólnienia, takiego jak powiedzenie pliku o nazwie „UIViewExtensions” .swift that wyrażają brak celu ”, gdy celem są ogólne rozszerzenia dla wszystkich UIViews.
Mark A. Donohoe
0

Zamiast dodawać moje komentarze w każdym miejscu, ujawniam je wszystkie tutaj w jednej odpowiedzi.

Osobiście stosuję podejście hybrydowe, które zapewnia zarówno dobrą użyteczność, jak i przejrzystość, a jednocześnie nie zaśmieca powierzchni interfejsu API dla obiektu, który rozszerzam.

Na przykład wszystko, co ma sens, aby być dostępne dla dowolnego ciągu, powinno znaleźć się w StringExtensions.swifttakich jak trimRight()i removeBlankLines().

Jednak gdybym miał funkcję rozszerzenia takie jak formatAsAccountNumber()byłoby nie pójść w tym pliku, ponieważ „Numer rachunku” nie jest czymś, co naturalnie zastosować do każdego / wszystkie sznurki i ma sens tylko w kontekście rachunków. W takim przypadku utworzyłbym plik o nazwie Strings+AccountFormatting.swiftlub nawet Strings+CustomFormatting.swiftz formatAsAccountNumber()funkcją, jeśli istnieje kilka typów / sposobów jego sformatowania.

Właściwie w tym ostatnim przykładzie aktywnie odradzam mojemu zespołowi używanie takich rozszerzeń w pierwszej kolejności i zamiast tego zachęcam do czegoś podobnego, AccountNumberFormatter.format(String)ponieważ w ogóle nie dotyka to Stringpowierzchni interfejsu API, a nie powinno. Wyjątkiem byłoby zdefiniowanie tego rozszerzenia w tym samym pliku, w którym jest używane, ale i tak nie miałoby ono własnej nazwy pliku.

Mark A. Donohoe
źródło
0

Wolę mieć znak +podkreślający fakt, że zawiera rozszerzenia:

String+Extensions.swift

A jeśli plik stanie się zbyt duży, możesz go podzielić w każdym celu:

String+UTF8Data.swift

String+Encrypt.swift

Xys
źródło