Старые надежные структуры данных и их противоречивый доступ (запись).
Использование объектов в качестве структур данных - это устоявшаяся практика, которая порождает множество проблем, связанных с ремонтопригодностью и развитием программного обеспечения, и злоупотребляет блестящими концепциями, сформулированными пять десятилетий назад. В этой первой части мы рассмотрим доступ к записи для этих объектов.
В своей классической статье 1972 года Дэвид Парнас определил новую и основополагающую концепцию современной разработки программного обеспечения: Скрытие информации.
Правило простое:
Если мы скроем нашу реализацию, мы сможем изменять ее сколько угодно раз.
До статьи Парнаса не существовало четких правил доступа к информации, и не было сомнительной практики погружаться в структуры данных, наказывая любые изменения ужасным волновым эффектом.
Давайте посмотрим, как смоделировать декартову точку:
Любой программный компонент, который манипулирует этими точками, будет связан с сохранением значений в виде декартовых координат x и y (Случайная реализация).
Поскольку это просто структура данных без операций, семантика атрибута будет разной в зависимости от критерия каждого программиста.
Следовательно, если мы хотим изменить случайную реализацию точки на ее полярные координаты, аналогично:
Поскольку это одна и та же точка в реальном мире, она обязательно должна быть представлена одним и тем же объектом в нашей биекции.
Биекция всегда зависит от субъективности аспектов, которые мы пытаемся моделировать. Чтобы нарисовать многоугольник, декартова (1, 1) и полярная (√2, π / 8) точки являются одной и той же точкой.
Случай попытки представить несколько возможных математических представлений был бы другим, если бы мы программировали семантику Wolfram. В таком случае представление является частью проблемы, т. Е. Они будут моделироваться разными объектами.
Решение простое. Скрыть внутреннее представление.
Как и предсказывал Парнас, многие проблемы с ремонтопригодностью кода были устранены путем инкапсуляции решений в модули, которые их определяют. В этом вся суть этой великолепной бумаги.
Следующий эволюционный шаг
С появлением объектно-ориентированного программирования концепции инкапсуляции и сокрытия информации были доведены до атомарного предела. Мы больше не говорим об инкапсуляции в модуле, а в том же объекте.
Возвращаясь к предыдущему примеру, переходим от:
в сторону изменения представительства:
В хорошем дизайне объекты связаны с обязанностями (интерфейсами), а не с представлениями.
Следовательно, если мы определим хороший точечный интерфейс, они могут произвольно изменять свое представление (даже во время выполнения) без распространения эффекта пульсации.
при изменении представления…
… Все продолжает работать правильно.
Алгоритмы и данные
Если бы мы работали по старому правилу:
программы = алгоритмы + структуры данных
… Тогда мы могли бы создавать отличное программное обеспечение с помощью сеттеров и геттеров.
В этой статье предполагается, что мы стремимся построить с декларативными объектами модели, реализация которых скрывается за обязанностями объектов.
Эти обязанности будут одинаковыми в отношении взаимного соответствия между этими объектами и реальным миром.
Инволюция
Несмотря на преимущества, перечисленные в приведенных выше примерах, текущий уровень техники показывает нам множество проблем, связанных с эффектом сцепления и пульсаций. Большинство из них вызвано укоренившейся привычкой использовать сеттеры и геттеры (или просто: аксессоры).
Давайте посмотрим на сеттеры и геттеры как на отдельные проблемы.
Сеттеры
Изменение внутреннего состояния объекта нарушает принцип неизменности. Это не рекомендуется, поскольку в реальном мире объекты не изменяются по своей сущности.
Единственный метод, разрешенный для записи в атрибуты, - это атомарная инициализация. С этого момента переменные должны быть доступны только для чтения.
Если мы будем придерживаться принципа взаимного однозначности, мы заметим, что в реальном мире никогда не бывает сообщений с формой setAttribute .. (). Это уловки реализации, которые используют программисты, и они ломают хорошие модели.
Мы никогда не сможем объяснить бизнес-эксперту, какую ответственность несут эти методы от названия.
Представим себе многоугольник как структуру данных.
Предположим, что у многоугольника не менее трех вершин.
Поскольку мы являемся структурой данных, мы не можем наложить такое ограничение.
Используя нашу замечательную среду IDE с автоматической генерацией кода, мы добавляем в нее сеттеры и геттеры.
Попробуем добавить ограничение на количество вершин в конструкторе:
Отныне будет невозможно создать многоугольник с менее чем тремя сторонами, таким образом выполняя взаимно однозначное соответствие с реальным миром евклидовой геометрии.
Если мы не используем наш сеттер…
Нам ничего не мешает запустить этот код:
На данный момент у нас есть два варианта:
- Дублируйте бизнес-логику в конструкторе и в сеттере.
- Устранение установщика навсегда в пользу неизменности
В случае принятия повторяющегося кода эффект пульсации начинает распространяться, когда наши ограничения растут. Например, если мы сделаем предусловие еще сильнее:
Предположим, что многоугольник имеет как минимум три разные вершины.
Правильный ответ, согласно нашим аксиомам дизайна, - второй.
Повторяющаяся или отсутствующая логика проверки инварианта
Многие объекты имеют инварианты, которые гарантируют их связность и достоверность представления для поддержания взаимного однозначности реального мира. Разрешение частичной настройки (атрибута) заставит нас контролировать инварианты представления в более чем одном месте, генерируя повторяющийся код, который всегда подвержен ошибкам при изменении ссылки и игнорировании другие ссылки.
Код генерируется без контроля
Многие среды разработки дают нам возможность автоматизировать создание сеттеров и получателей. Это приводит к тому, что новые поколения программистов думают, что это хорошая практика проектирования, порождая пороки, которые трудно исправить.
Это средство распространяет проблему, наличие этого инструмента дает ощущение, что это общепринятая практика.
Рекомендации
- Не используйте сеттеры. Для этого нет веских причин.
- Наличие методов с именем setSomething… () - это запах кода.
- Не имеют публичных атрибутов. Для практических целей это все равно, что иметь сеттеры и геттеры.
- Не имеют общедоступных статических атрибутов. В дополнение к тому, что упомянуто выше, классы должны быть без состояния, и это запах кода, показывающий класс, который используется как глобальная переменная.
- Избегайте анемичных объектов (содержащих только атрибуты без ответственности). Это запах кода, намекающий на отсутствие объекта на биекцию.
Выводы
Использование сеттеров создает связь и предотвращает постепенную эволюцию наших компьютерных систем. По аргументам, изложенным в этой статье, мы должны максимально ограничить его использование.
Геттеры
Как и в случае с сеттерами, геттеры не приветствуются. Мы подробно рассмотрим эту тему в этой статье:
Частично цель этой серии статей - создать пространство для дебатов и дискуссий по дизайну программного обеспечения.
Ждем комментариев и предложений по этой статье.
Эта статья также доступна на испанском здесь.