Физический акт написания кода прост. Для этого не требуется ничего, кроме клавиатуры и текстового редактора. Конечно, если вы хотите, чтобы ваша программа что-то делала, вы должны понимать самые основные элементы языка, который вы пишете - это несложно. И все же программная инженерия сложна. Я знаю это, потому что написал код, который не понял всего через несколько месяцев. Умножьте это на десять человек, работающих над любой данной базой кода, и вы получите большой беспорядок, который трудно понять. Если вы видели Разговор Ричарда Хикки о простоте, это метафорический слон, который является вашей кодовой базой. Проблема не в том, что мы не умеем писать код, проблема в том, что нас никто никогда не учил дизайну.

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

Дон Норман. «Дизайн повседневных вещей».

Все, с чем мы взаимодействуем ежедневно, было разработано - включая код, который мы пишем и поддерживаем. Когда что-то спроектировано хорошо, работать с ним интуитивно понятно. Мы делаем меньше ошибок, поскольку эффективно используем эту вещь для достижения наших целей. С другой стороны, когда что-то плохо спроектировано, очень легко сделать ошибку. Мы виним себя и часто расстраиваемся. Плохо спроектированный код неприятен для работы, потому что его трудно использовать правильно; Если вы хотите писать высококачественный код, важно понимать, как применять дизайн к коду.

Применение концепций дизайна к программному обеспечению

Двумя наиболее важными характеристиками хорошего дизайна являются открываемость и понимание.

Дон Норман. «Дизайн повседневных вещей».

В своей книге «Дизайн повседневных вещей» Дон Норман разбивает дизайн на шесть компонентов. Каждый из шести компонентов подпадает под одну из двух категорий: открываемость и понимание. Давайте исследуем каждый из компонентов дизайна: 1) определив, что это такое; 2) Глядя на пример из реального мира; и 3) Рассмотрение примера в коде.

Обнаруживаемость

Понимание

Обнаруживаемость

Компонент дизайна 1: Возможности

«Аффорданс - это взаимосвязь между свойствами объекта и возможностями агента, которые определяют, как объект может быть использован».

Дон Норман. «Дизайн повседневных вещей».

В реальном мире. Стекло позволяет видеть сквозь него. Ручка на этой двери дает возможность открыть дверь.

В коде: методы и свойства являются аффордансами. Они помогают информировать о возможностях кода, с которым мы взаимодействуем.

Пример

Ниже приведен фрагмент интерфейса для UIImageView. Свойства и методы, доступные в интерфейсе, предоставляют возможность установить одно изображение или заставить представление изображения анимировать коллекцию изображений.

class UIImageView : UIView {

    /**
     Returns an image view initialized with the specified image.
     */
    public init(image: UIImage?)

    /**
     The amount of time it takes to go through one cycle of the images.
     */
    var animationDuration: TimeInterval
    
    /**
     An array of UIImage objects to use for an animation.
     */
    var animationImages: [UIImage]?

    /**
     The image displayed in the image view.
     */
    var image: UIImage?
}

Компонент дизайна 2: означающие

«Термин« означающий »относится к любому знаку или звуку, любому воспринимаемому индикатору, который сообщает человеку о надлежащем поведении».

Дон Норман. «Дизайн повседневных вещей».

В реальном мире: когда на двери есть табличка с указанием «потянуть», это означает.

В коде. Знаки имеют форму комментариев и модификаторов. Некоторые примеры означающих в Swift: struct, class, enum, private, var и let.

Пример

Ниже представлена ​​собственность от UICollectionView.

/**
 The value of this property is an array of NSIndexPath objects, each of which corresponds to a single selected item. If there are no selected items, the value of this property is nil.
 */
var indexPathsForSelectedItems: [IndexPath]? { get }

Есть несколько признаков, которые дают нам контекст о том, как использовать это свойство:

  • var указывает, что это свойство изменяемое.
  • get указывает, что это свойство только для чтения.
  • ? указывает, что это свойство может быть установлено на nil.
  • Комментарий к этому свойству указывает, когда мы можем ожидать, что это свойство будет nil.

Компонент проекта 3: ограничения

«Ограничения - это мощные подсказки, ограничивающие набор возможных действий»

Дон Норман. «Дизайн повседневных вещей».

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

В коде: внедрение зависимостей и строго типизированные языки действуют как ограничения.

Пример

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

init(configuration: URLSessionConfiguration)

Компонент проекта 4: Сопоставления

«Отображение - это технический термин, заимствованный из математики, означающий взаимосвязь между элементами двух наборов вещей».

Дон Норман. «Дизайн повседневных вещей».

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

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

  • Мы можем сопоставить набор классов с конкретным шаблоном проектирования (например, модель-представление-контроллер).
  • Мы можем сопоставить наши модели с объектом реального мира (например, человеком).
  • Мы можем сопоставить наш код с конкретным алгоритмом, который мы реализуем.

Пример

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

* Homes (HMHome) are the top level container, and represent a structure that a user would generally consider to be a single home. Users might have multiple homes that are far apart, such as a primary home and a vacation home. Or they might have two homes that are close together, but that they consider different homes—for example, a main home and a guest cottage on the same property.

* Rooms (HMRoom) are optional parts of homes, and represent individual rooms in the home. Rooms don't have any physical characteristics-size, location, etc. They’re simply names that are meaningful to the user, such as "living room" or "kitchen". Meaningful room names enable commands like, "Siri, turn on the kitchen lights."

Компонент дизайна 5: обратная связь

«Плохая обратная связь может быть хуже, чем ее отсутствие вообще, потому что она отвлекает, неинформативна и во многих случаях раздражает и вызывает беспокойство».

Дон Норман. «Дизайн повседневных вещей».

В реальном мире: если вы вставите кредитную карту с чипом в платежный терминал местного продавца, терминал в конечном итоге подаст звуковой сигнал, когда придет время извлекать карту. Это отзыв.

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

Пример

Если вы когда-либо использовали UICollectionView или UITableView раньше, вы, вероятно, сталкивались с такой ошибкой:

Завершение работы приложения из-за неперехваченного исключения «NSInternalInconsistencyException», причина: «попытка вставить строку в раздел 0, но после обновления в разделе 0 осталось только 0 строк»

Это хороший пример UIKit обратной связи. UIKit разработан для того, чтобы ваше приложение вылетело как можно скорее, чтобы вы знали, что сделали предположение о структуре ваших данных, несовместимое с тем, как работает UICollectionView или UITableView.

Понимание

Компонент проекта 6: концептуальные модели

«Концептуальная модель - это объяснение, обычно очень упрощенное, того, как что-то работает».

Дон Норман. «Дизайн повседневных вещей».

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

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

Каждый раз, когда вы взаимодействуете с API в коде, вы начинаете с ряда ожиданий относительно того, как код будет работать. Это ваша концептуальная модель. По мере того, как вы больше взаимодействуете с кодом, вы обновляете свою концептуальную модель, чтобы отразить свое новое понимание. Например, передача параметра nil методу может привести к сбою кода. Если это произойдет, ваша концептуальная модель изменится, чтобы учесть идею о том, что nil не является допустимым параметром для передачи методу, с которым вы взаимодействуете. Обратите внимание, что в этом примере, когда происходит сбой кода (т.е. обратная связь), мы обновляем нашу концептуальную модель. Точно так же мы используем каждый из других компонентов дизайна (аффордансы, означающие, ограничения, сопоставления и обратную связь) для изменения наших концептуальных моделей с течением времени.

Пример

NotificationCenter в iOS и macOS использует шаблон наблюдателя.

Это краткое заявление, которое легко передает много информации любому, кто знает, что такое паттерн наблюдателя. Несмотря на то, что в документации для NotificationCenter явно не указано, что он использует шаблон наблюдателя, открытые методы дают подсказки любому, кто работал с шаблоном наблюдателя.

/**
 Adds an entry to the notification center's dispatch table with an observer and a notification selector, and an optional notification name and sender.
 */
func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)
/**
 Removes all entries specifying a given observer from the notification center's dispatch table.
 */
func removeObserver(_ observer: Any)

Дизайн нужен не только для красивого интерфейса

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