Собственное соглашение об именах (или присвоении имен классам) в структурах Swift (для iOS)

TL;DR

  • Если фреймворк называется AEConicalGradient, должны ли классы в нем называться ConicalGradientLayer и ConicalGradientView или просто Layer strong > и Просмотр?

  • Если фреймворк называется AELog, и он имеет класс, также называемый AELog, а другой класс Линия с некоторым обратным вызовом делегата, который использует этот тип Линия, как реализовать этот делегат, если ваш код также определил тип с именем Линия, поскольку добавление имени фрейма в качестве префикса в подпись метода func didLog(line: AELog.Line) будет делать error 'Line' is not a member type of 'AELog', который на самом деле не является (частью структуры AELog, но не является членом класса AELog).

Подробнее

При обновлении нескольких платформ для Swift 3 у меня появилось много других соображений о том, что такое правильное соглашение об именах для классов Framework в зависимости от самого имени фрейма.

В конце я понял, что на самом деле я не использовал одно и то же соглашение для разных фреймворков, поэтому теперь мне не нравится эта непротиворечивость.

Я перейду прямо к некоторым реальным примерам, с которыми я столкнулся:

1. Пример: Framework не имеет класса с именем в качестве имени фрейма:

Framework называется AEConicalGradient и состоит из 2 открытых классов:

  • Подкласс CALayer
  • Подкласс UIView

Какое должно быть имя для каждого из этих классов?

a) AEConicalGradientLayer и AEConicalGradientView

b) ConicalGradientLayer и ConicalGradientView

c) Слой и Просмотр

Обсуждение:

a) Я использовал это в первой версии фреймворка с обоими этими классами в одном файле AEConicalGradient.swift. Мне больше не нравится этот подход, я разделяю источники на несколько файлов, поэтому теперь я думаю о подходах b > и c).

b) В этом подходе мне нравится тот факт, что кто-то может просто перетащить эти файлы в проект (например, без использования какого-либо менеджера зависимостей) или скопировать/вставить код, и он слишком сильно не потеряет контекст (из-за четких имен) но с другой стороны это звучит немного повторяемо, если вы думаете об этом как AEConicalGradient.ConicalGradientLayer и AEConicalGradient.ConicalGradientView.

c) AEConicalGradient.Layer и AEConicalGradient.View звучит так, как я этого хочу, но с другой стороны, они с большей вероятностью могут вступить в конфликт, если кто-то их собственные классы Layer или View, или если они просто перетаскивают файлы или копируют/вставляют контекст кода, в значительной степени теряются (например, что это за Layer или Просмотр для?).

2. Пример: Framework имеет класс с именем в качестве имени фрейма:

Framework называется AELog и состоит из 3 общедоступных классов и протокола делегатов:

  • Общий экземпляр (и делегат для него)
  • Модель строки журнала
  • Config

Обсуждение:

a) Как и в первом примере в первой версии фреймворка, я имел классы с именем AELog, AELogDelegate, AELogLine и AELogConfig - все внутри одного файла AELog.swift.

b) В последнем обновлении для Swift 3 у меня появилось несколько файлов и имена классов: AELog, AELogDelegate, Линия и Config.

Это, когда я понял, что если вы import AELog и реализуете AELogDelegate (у которого есть только func didLog(line: Line)) И у вас уже есть другой класс Line, определенный в вашем собственном модуле , это приведет к конфликту, который может быть решена только путем переименования одного из двух классов Line!

Честно говоря, я действительно не понимаю, как этого избежать. Поскольку, если вы попытаетесь использовать тип пространства имен в методе делегата, например func didLog(line: AELog.Line), вы получите ошибку 'Line' is not a member type of 'AELog', которая на самом деле не является (она является частью структуры AELog, но не является членом AELog class внутри.)

Это, когда я начал немного беспокоиться об этом "свободном" наименовании. Любые предложения или мнения по этому конкретному вопросу?

c) В духе очень "свободного" наименования, например, в 1.c), можно ли переименовать класс AELog в класс Log и AELogDelegate только Делегат? Будет ли эта ошибка исправления указана в 2.b), потому что нет класса с именем, аналогичным имени фрейма, поэтому, возможно, компилятор распознает AELog.Line?

Вопрос:
Каковы некоторые аргументы "чистого кода" для любого из этих подходов? Есть ли очевидный "правильный" способ, как это сделать? Я пропустил что-то важное здесь?

Ответ 1

TL; DR

  • Имена типов должны быть ConicalGradientView и ConicalGradientLayer. "Вид" и "Слой" просто недостаточно понятны для случая концепции "вид" или "слой".

  • Невозможно, чтобы компилятор Swift не мог устранить конфликтное имя модуля/типа в этом контексте. Вы не найдете код от Apple или от третьих сторон, которые используют конфликтующие имена для фреймворков/модулей против типов внутри них. Это может быть по технической причине, но это также будет путать, и ваш случай "AELog" как типа типа Swift также не является идиоматическим.

Длинный ответ

Имя типа должно каким-то образом описать его роль в программе, в которой вы используете тип. В то время как это, конечно, субъективно, я не думайте, что "Вид" - очень описательное имя для подкласса вида, поскольку в Cocoa существуют десятки подклассов NSView/UIView, а просмотры выполняют потенциально сотни ролей в вашем собственном приложении. Таким образом, по логике, что, только видя это неквалифицированное имя типа, вы не можете действительно догадываться о том, что он делает, поэтому оно не может быть хорошим именем. Функция имени модуля/имени пространства больше предназначена для обеспечения минимального уровня неоднозначности, что приводит к поведению undefined при столкновении имен (роскошь, традиционно недоступная в C/Objective-C), но, возможно, ее роль не должна быть обычной используется в полной форме.

Подумайте об этом также с точки зрения "что, если я хотел бы представить новый вариант представления в моей структуре?" - отдельная структура для конкретных конических градиентов, например, звучит немного смешно. Рамки, возможно, для разных видов представлений, которые имеют градиент к ним, звучат как более разумный "минимальный размер" для фреймворка (даже если на данный момент вы можете использовать только конический случай для ваших нужд), и это также относится к концепцию типа "Слой". Если "AEGradientView", например, является именем вашей структуры, тогда вы можете иметь такие вещи, как ConicalGradientView, RadialGradientView и т.д., И это будет хорошо прочитано. Наконец, если вы хотите сделать что-то более кратким при использовании API из этой структуры, вы всегда можете использовать typealias.

Таким образом, быть достаточно описательным является одним из факторов, но, конечно, краткость - это другой фактор, который вы хотите сбалансировать. Если у вас действительно действительно есть случай типа, который действительно уникален в структуре, и имя типа может быть как кратким, так и четко обозначающим его роль, тогда моя конвенция, по крайней мере, действительно использует самое краткое возможное имя. Так, например, в определенной структуре обработки изображений и метаданных, которые я разработал совместно, Image - это имя используемого типа как контейнер для данных изображения и метаданных. потому что там действительно в лингви этой структуры только когда-либо один класс в роли "изображения", и, в решающей степени, он четко описывает, для чего предназначен класс (в отличие, например, "Вид" ). Точно так же я использую кратчайшее возможное имя для вложенных типов, поэтому, например, тип, используемый для обозначения "ошибки изображения" (перечисление, соответствующее Swift.Error), называется Carpaccio.Image.Error.

Что касается вашего второго вопроса о конфликте имени модуля/типа, эта неоднозначность не может быть разрешена компилятором в этом контексте. Вы также действительно не найдете каких-либо экземпляров модуля и типа, которые будут запутаны в любой из инфраструктур Apple (и я не думаю, что столкнулся с любым примером этого в стороннем коде). Помимо технической причины, по которой у компилятора, скорее всего, нет возможности устранить неоднозначность между модулем и именем класса, связанным с ним, он также путает программиста следующие соглашения о платформе: подумайте о имени модуля как имени "продукта" и типов внутри как составные части, в то время как AELog является хорошим именем для каротажной структуры в Swift, это не было бы идиоматическим именем типа в коде Swift; Префиксы имен, такие как "AE", просто не используются в Swift для типов - они даже не нужны для подклассов Objective-C, которые вы создаете в Swift, потому что полное имя класса, видимое в среде выполнения Objective-C, фактически включает имя модуля в качестве префикса.

Там хороший разговор WWDC с начала этого года также по соображениям об именах Swift API, обсуждая некоторые из вышеперечисленных.

Ответ 2

Вы не сможете понравиться всем. Запомни. Таким образом, возможно, вопрос должен быть "то, что лучше всего подходит для большинства"?

(1) Резервирование a la "AEConicalGradient.ConicalGradientView" не делает.

(2) Существует несколько способов, которыми разработчик может импортировать ваш класс "Layer" в свой проект. Вам нужно убедиться, что они могут выполнить свою работу без дополнительной сложности. Я бы воспользовался самым "общим" способом импорта - импортировал всю вашу инфраструктуру, а не отдельный файл, что-то вроде импорта файла "Layer".

(3) Я показываю свой возраст, но в Xcode нет ничего подобного файлам "include".

Начиная с этого, я бы сказал, держите вещи как можно более универсальными на самом глубоком уровне - создайте "слой" файл и класс, и пусть пользователь выполнит ручные изменения, необходимые, чтобы сделать его "AELayer", если они решат импортируйте один файл. В конце концов, они захватывают один файл, верно?

Итак, мой выбор для иерархии:

AEFramework

Каноническое

     
  

Слой

         

Вид

  
  

Журнал

     
  

делегат

         

Линия

         

Конфигурация

  

Ваш выбор относительно того, что должно быть в отдельных файлах. Здесь моя предлагаемая настройка рамки:

Файл "AEFramework.swift" (это также ваша цель):

public class Canonical {

}

Файл "Canonical.swift":

extension Canonical {
    public class View: UIView {

    }
    public class Layer: CALayer {

    }
}

Файл "Layer.swift":

extension Canoncial.Layer {

}

Самый простой способ использовать вещи:

import AEFramework
let myLayer = Canoncial.Layer()

Самый сложный способ заключается в изменении "Layer.swift" после добавления его в ваш проект:

extension CALayer {

}