Как лучше всего именовать файлы Swift, которые добавляют расширения к существующим объектам?

167

Можно добавить расширения к существующим типам объектов Swift, используя расширения, как описано в спецификации языка .

В результате возможно создание расширений, таких как:

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

Тем не менее, какова наилучшая практика именования исходных файлов Swift, содержащих такие расширения?

В прошлом, соглашение было использовать extendedtype+categoryname.mдля типа Objective-C , как описано в руководстве по Objective-C . Но в примере Swift нет названия категории, и называть его String.swiftнеуместно.

Таким образом, вопрос заключается в следующем: с учетом вышеуказанного Stringрасширения, как должен называться исходный файл swift?

AlBlue
источник
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он не имеет большого грамматического смысла.

Picciano
источник
2
Хотя можно заключить, что расширяется соглашением об именах, это IHMO - ненужное осложнение. Вместо тонны файлов <name> + <extention> .swift у меня есть один файл extensions.swift, который я обычно использую для каждого проекта. Внутренний файл организован так, что найти конкретный расширенный класс легко.
Йогст
18
Этот ответ, <name> + <extention> .swift, действительно является тем способом, которым Xcode делает это при создании подклассов NSManagedObject для Core Data в Xcode 8. Пример: Foo + CoreDataProperties.swift.
Джерри Кринок
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 'Strings' в другом месте.
Марк А. Донохо
1

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

Еще одна вещь, объединение одинаковых файлов в один сделает ваше здание быстрее. Обратитесь к разделу «Оптимизация-Сдвиг-Время сборки»

DawnSong
источник
1
Это имеет два соглашения об именах файлов для одной и той же вещи. Я думаю, что это плохо.
значение имеет значение
@ значение-вопросы это зависит. Соглашение об именах хорошо известно и рекомендовано Apple Documents. Делай как хочешь.
DawnSong
Хотелось бы, чтобы больше программистов стремились к элегантности, ограничивая вариации имен и форматирования кода.
значение имеет значение
@ смысл-дела Элегантность имеет две стороны, это как классическая спорная проблема о том, как писать фигурные скобки на C-подобных языках. Это тривиально, поэтому я не думаю, что нужно выбирать один и делать его обязательным, пока большинство людей не согласится на это.
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 в этом примере), но на мой взгляд, это чистое решение с точки зрения разделения интерфейсов.

Я думаю, что этот подход одинаково хорошо работает как с внешними классами, так и с вашими.

Faisal Memon
источник
0

Почему это даже спор? Должен ли я поместить все мои подклассы в файл с именем _Subclasses.swift. Думаю, нет. Swift имеет основанный на модуле интервал имен. Для расширения хорошо известного класса Swift необходим файл, который соответствует его назначению. У меня могла бы быть большая команда, которая создает файл UIViewExtensions.swift, который не выражает никакой цели и может сбить с толку разработчиков и может быть легко продублирован в проекте, который не будет создаваться. Соглашение об именах Objective-C работает нормально, и пока Swift не получит реальный интервал имен, это лучший путь.

Том Кондон
источник
В моем случае, я думаю, что имеет смысл иметь файл с именем UIViewExtensions.swift, при условии, что расширения, определенные в этом файле, будут иметь смысл для любого / всех классов UIView, таких как метод placeIn (UIView). Если он специфичен для использования (т. Е. Только для части приложения, скажем, вокруг оформления пользовательского представления, то я бы сделал UIView + CustomDecoration.swift. Суть в том, что вам нужно рассмотреть использование, прежде чем делать обобщение, например, произнесение файла с именем 'UIViewExtensions .swift, которые не выражают никакой цели », когда целью является общее расширение для всех UIViews.
Mark A. Donohoe
0

Вместо того, чтобы добавлять свои комментарии повсюду, я выкладываю их здесь в одном ответе.

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

Например, все, что имеет смысл быть доступным для какой-либо строки, будет содержать, StringExtensions.swiftнапример, trimRight()и removeBlankLines().

Тем не менее, если бы у меня была функция расширения, такая как formatAsAccountNumber()она не была бы в этом файле, потому что «Номер счета» не является чем-то, что естественно применимо к любым / всем строкам и имеет смысл только в контексте учетных записей. В этом случае я бы создал файл с именем Strings+AccountFormatting.swiftили, может быть, даже Strings+CustomFormatting.swiftс formatAsAccountNumber()функцией, если есть несколько типов / способов для его форматирования.

На самом деле, в этом последнем примере я активно отговаривал свою команду от использования подобных расширений, и вместо этого поощрял бы что-то вроде AccountNumberFormatter.format(String)этого, поскольку это вообще не затрагивает область Stringповерхности API, как это не должно быть. Исключением может быть то, что вы определили это расширение в том же файле, где оно используется, но в любом случае у него не будет собственного имени.

Марк А. Донохо
источник
0

Я предпочитаю +подчеркивать тот факт, что он содержит расширения:

String+Extensions.swift

И если файл становится слишком большим, вы можете разделить его для каждой цели:

String+UTF8Data.swift

String+Encrypt.swift

XYS
источник