Яка найкраща практика називати файли Swift, які додають розширення до існуючих об'єктів?


166

Можна додати розширення до існуючих типів об'єктів Swift, використовуючи розширення, як описано в специфікації мови .

Як результат, можна створити розширення, такі як:

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

Однак яка найкраща практика іменування вихідних файлів Swift, що містять такі розширення?

У минулому конвенція мала використовуватись extendedtype+categoryname.mдля типу "Об'єктив-С", про який говорилося в посібнику "Об'єктив-С" . Але приклад Swift не має назви категорії, і називати його String.swiftне здається доцільним.

Отже, питання: з огляду на вищезазначене Stringрозширення, як слід називати швидкий вихідний файл?


5
Це не питання перегляду коду - мене не цікавить цей конкретний приклад, я хочу знати, що таке швидка умова іменування.
AlBlue

2
Конвенції про іменування не існує. Єдине, що нам потрібно продовжувати - це категорії від Objective-C, які завжди дотримувались ClassName+ExtensionNameформату, і які я не бачу занадто багато людей досі використовують. Крім того, я вважаю, що незграбний замість того, щоб просто визначити класи та розширення разом, або надати файлу кращу назву на зразок FooAbleTypesта визначення екземплярів у сукупності.
CodaFi

4
Там немає НЕ іменування практика поки. Ось думка: згуртуйте всі глобальні розширення разом в єдине ціле Extensions.swift. Таким чином, ви не втратите їх слід, і новачки в кодовій базі негайно їх помітять. І я вважаю за краще зберігати разові розширення приватними для потрібного файлу.
Ендрю,

1
За словами Ендрю, поки що немає стандартної практики іменування - тому це питання було запропоновано спеціально отримати думки, щоб новостворена громада могла прийти до запропонованих ідей.
AlBlue

1
На мою думку, єдиний файл extensions.swift - це шлях. Тримайте структуру всередині неї організованою (по-своєму), щоб легко знаходити те, що вам потрібно. Один файл легко копіювати або посилати з різних проектів і не забувати речі.
Йост

Відповіді:


202

Більшість прикладів, які я бачив, імітують підхід Objective-C. Приклад розширення вище:

String+UTF8Data.swift

Переваги полягають у тому, що конвенція про іменування полегшує розуміння того, що це розширення і який клас поширюється.

Проблема з використанням Extensions.swift або навіть StringExtensions.swiftтим, що неможливо зробити призначення файлу за його ім'ям, не дивлячись на його вміст.

Використання xxxable.swiftпідходу, який використовується Java, працює добре для протоколів або розширень, які визначають лише методи. Але знову ж таки, приклад вище визначає атрибут, щоб UTF8Dataable.swiftвін не мав особливого граматичного сенсу.


2
Хоча можна зробити висновок про те, що розширюється конвенцією про іменування, це IHMO є непотрібним ускладненням. Замість тонн файлів <name> + <extens> .swift я зберігаю один файл extensions.swift, який я зазвичай використовую для кожного проекту. Файл внутрішньо організований таким чином, що знайти певний клас, який розширюється, легко.
Йост

18
Ця відповідь, <ім'я> + <продовження>.
Джеррі Крінок

5
Що робити, якщо розширення реалізує кілька методів?
AlexVPerl

2
Просто будьте максимально описовими. Наприклад, якщо у вас є розширення Image, яке включає різні функції застосування фільтрів, назвіть його Image + Filters.swift. Добре використовувати різні файли для споріднених груп на розширених функціях. Групуйте пов’язані речі разом, але незв’язані речі тримайте окремо. Життя буде добре.
picciano

Якщо ви використовуєте конвенцію ExtendedType+Functionality.swift, чи корисна практика сортувати всі Stringрозширення, наприклад, у власну підпапку (тобтоString чи String Extensions) під Extensionsпапку? Або краще просто зберегти всі файли розширень на одному рівні під Extensionsпапкою?
Ной Уайлдер

9

Не існує конвенції Свіфта. Не ускладнювати:

StringExtensions.swift

Я створюю один файл для кожного класу, який я розширюю. Якщо ви використовуєте один файл для всіх розширень, він швидко перетвориться в джунглі.


8
Це не видається особливо корисним для використання.
Келлер

1
У порівнянні з?
Майк Таверн

3
У порівнянні з файлом розширень класу, що окремо (або щільно пов'язаний), який служить єдиній (або прямо пов'язаній) цілі. Щось на кшталт "StringExtensions" звучить так, що воно може містити все, від санітарізації загальної мети до специфічної логіки, що може бути не найкращим підходом, якщо повторне використання викликає занепокоєння. Конвенція про назву какао схиляється до функціонування, а не до реалізації. Я б стверджував, що "StringExtensions" вказує на останнє. Іменуючи конвенцію убік, я віддаю перевагу прийнятій відповіді, звичайно, в ObjC, але у Swift це виглядає як ще кращий підхід завдяки модулям.
Келлер

2
Що має сенс. Я думав більше про єдине додаток, коли повторне використання не викликало проблем. Наприклад, скажіть, що у мене є кілька непов'язаних рядкових функцій, які я хочу використовувати як розширення - я міг би створити один файл і помістити всі ці функції туди, або створити один файл на функцію. Мені подобається простота одного файлу в такому випадку. Але ваші міркування є здоровими. Дякую.
Майк Таверн

Це має повний сенс, якщо умови, які тут додаються, природно застосовуватимуться до всіх рядків (наприклад, "trimRight ()" як приклад). Якщо це щось більше, що залежить від конкретного випадку (наприклад, "formatAccountNumber ()"), тоді файл повинен бути "Strings + AccountFormatting.swift", і його слід визначати лише там, де він використовується, щоб не захаращувати API рядків 'stringings' в іншому місці.
Марк А. Донохое

1

Я вважаю за краще, StringExtensions.swiftпоки я не додав занадто багато речей, щоб розділити файл на щось подібне String+utf8Data.swiftі String+Encrypt.swift.

І ще одне - об'єднання подібних файлів в один зробить вашу будівлю швидшою. Див. Розділ Optimizing-Swift-Build-Times


1
Це два режими іменування файлів для однієї і тієї ж речі. Я думаю, що це погано.
значення-питання

@ значення-питання Це залежить. Договір Apple називає обидва відомих і рекомендованих Apple Documents. Робіть як хочете.
СвітанокСонг

Я хотів би, щоб більше програмістів прагнуло до елегантності, обмеживши варіанти імен та форматів [форматування].
значення-справи

@ значення-питання Елегантність має дві сторони, це як класична суперечлива проблема щодо того, як писати фігурні дужки на мовах, схожих на С. Це банально, тому я не думаю, що потрібно вибирати його і робити його обов'язковим, поки більшість людей не погодиться на це.
DawnSong

Я мав на увазі елегантність консистенції: використовуючи один спосіб назви розширень або один спосіб розміщення фігурних брекетів. Тоді я думаю, що є помірна різниця в читанні різних фігурних фігурних брекетів; тому я не думаю, що це взагалі "банально".
значення-справи

0

Якщо у вас є узгоджений командою набір загальних та різноманітних удосконалень, їх згуртовування, як Extensions.swift, працює як рішення першого рівня Keep-It-Simple. Однак у міру того, як ваша складність зростає або розширення стають все більш задіяними, необхідна ієрархія для інкапсулювання складності. За таких обставин я рекомендую наступну практику з прикладом.

У мене був клас, який розмовляв із моїм бек-ендом Server. Він почав зростати, щоб охопити два різних цільових додатки. Дехто любить великий файл, але просто логічно розділений на розширення. Я вважаю за краще тримати кожен файл порівняно коротко, тому я вибрав наступне рішення. Serverспочатку відповідав CloudAdapterProtocolі впроваджував усі його методи. Що я зробив, це перетворити протокол на ієрархію, змусивши його посилатися на підпорядковані протоколи:

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

У Server.swiftмене є

import Foundation
import UIKit
import Alamofire
import AlamofireImage

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

Server.swiftтоді просто реалізується API сервера для настройки сервера та отримання версії API. Справжня робота розділена на два файли:

Server_ReggyCloudProtocol.swift
Server_ProReggyCloudProtocol.swift

Вони реалізують відповідні протоколи.

Це означає, що вам потрібно мати декларації про імпорт в інших файлах (для Alamofire в цьому прикладі), але це чисте рішення з точки зору розділення інтерфейсів, на мій погляд.

Я думаю, що цей підхід однаково добре працює як із зовнішньо визначеними класами, так і з вашим власним.


0

Чому це навіть дискусія? Чи повинен я помістити всі свої підкласи у файл під назвою _Subclasses.swift. Я думаю, що не. У Swift є інтервал назви модулів. Для розширення відомого класу Swift потрібен файл, специфічний за його призначенням. Я міг би створити велику команду, яка створює файл, який є UIViewExtensions.swift, який не виражає мети і заплутає розробників, і його можна легко дублювати в проекті, який не будується. Конвенція про іменування Objective-C працює чудово, і поки Swift не має реального інтервалу імен, це найкращий шлях.


У моєму випадку я думаю, що має сенс мати файл під назвою UIViewExtensions.swift за умови, що розширення, визначені у цьому файлі, мають сенс для будь-яких / всіх класів UIView, наприклад методу 'placeIn (UIView)'. Якщо це специфічно для використання (тобто лише для частини програми, скажімо, навколо прикраси власного вигляду, тоді я б зробив UIView + CustomDecoration.swift. Справа в тому, що вам доведеться розглянути використання перед тим, як зробити узагальнення, як вимовити файл під назвою "UIViewExtensions" .Swift, які не виражають мети ", коли ціль є загальним розширенням для всіх UIViews.
Марк А. Донохоє,

0

Замість того, щоб додавати свої коментарі повсюдно, я тут їх викриваю в одній відповіді.

Особисто я приймаю гібридний підхід, який дає як хорошу зручність використання, так і чіткість, а також не захаращуючи площу поверхні API для об'єкта, який я розширюю.

Наприклад, все, що має сенс бути доступним для будь-якого рядка, буде входити в StringExtensions.swiftтакі, як trimRight()іremoveBlankLines() .

Однак, якби у мене була така функція розширення, як formatAsAccountNumber()вона не входила б у цей файл, оскільки "Номер рахунку" - це не те, що природно стосується будь-яких / всіх рядків і має сенс лише в контексті облікових записів. У такому випадку я створив би файл, який називається Strings+AccountFormatting.swiftабо, можливо, навіть Strings+CustomFormatting.swiftз aformatAsAccountNumber() функцією , якщо існує кілька типів / способи фактично форматувати його.

Власне, в цьому останньому прикладі я активно відмовляю свою команду від використання таких розширень в першу чергу, і натомість заохочую щось на зразок, AccountNumberFormatter.format(String)оскільки це зовсім не торкається Stringповерхні поверхні API, як це не повинно. Виняток буде, якби ви визначили це розширення в тому самому файлі, де воно використовується, але тоді б у нього не було власного імені файлу.


0

Я вважаю за +краще підкреслити той факт, що він містить розширення:

String+Extensions.swift

І якщо файл стає занадто великим, ви можете розділити його на кожну мету:

String+UTF8Data.swift

String+Encrypt.swift

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.