Физический акт написания кода прост. Для этого не требуется ничего, кроме клавиатуры и текстового редактора. Конечно, если вы хотите, чтобы ваша программа что-то делала, вы должны понимать самые основные элементы языка, который вы пишете - это несложно. И все же программная инженерия сложна. Я знаю это, потому что написал код, который не понял всего через несколько месяцев. Умножьте это на десять человек, работающих над любой данной базой кода, и вы получите большой беспорядок, который трудно понять. Если вы видели Разговор Ричарда Хикки о простоте, это метафорический слон, который является вашей кодовой базой. Проблема не в том, что мы не умеем писать код, проблема в том, что нас никто никогда не учил дизайну.
Все искусственные вещи созданы. Будь то расположение мебели в комнате, дорожки через сад или лес, или сложности электронного устройства, какой-то человек или группа людей должны были решить, каким будет расположение, принцип действия и механизмы.
Дон Норман. «Дизайн повседневных вещей».
Все, с чем мы взаимодействуем ежедневно, было разработано - включая код, который мы пишем и поддерживаем. Когда что-то спроектировано хорошо, работать с ним интуитивно понятно. Мы делаем меньше ошибок, поскольку эффективно используем эту вещь для достижения наших целей. С другой стороны, когда что-то плохо спроектировано, очень легко сделать ошибку. Мы виним себя и часто расстраиваемся. Плохо спроектированный код неприятен для работы, потому что его трудно использовать правильно; Если вы хотите писать высококачественный код, важно понимать, как применять дизайн к коду.
Применение концепций дизайна к программному обеспечению
Двумя наиболее важными характеристиками хорошего дизайна являются открываемость и понимание.
Дон Норман. «Дизайн повседневных вещей».
В своей книге «Дизайн повседневных вещей» Дон Норман разбивает дизайн на шесть компонентов. Каждый из шести компонентов подпадает под одну из двух категорий: открываемость и понимание. Давайте исследуем каждый из компонентов дизайна: 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)
Дизайн нужен не только для красивого интерфейса
Концепции дизайна в этой статье не позволяют однозначно сказать нам, что делает код хорошим или плохим. Вместо этого они обеспечивают основу для понимания того, какое значение мы придаем другим с помощью кода, который мы пишем. Каждый символ в нашей кодовой базе может быть охарактеризован одним или несколькими из этих компонентов дизайна и передает смысл человеку, взаимодействующему с кодом. Без твердого понимания дизайна мы, возможно, не сможем передать предполагаемое сообщение будущим разработчикам нашего кода, в том числе и нам самим!