6 декабря 2013 в 15:11

POKA-YOKE проектирование: от «запаха» к благоуханию перевод

От переводчика. Это перевод серии постов из блога Марка Симана. Я не хочу объединять некоторые из постов, несмотря на то, что они небольшие по размеру, а просто постараюсь соблюсти структуру, предложенную Марком.

Ещё немного от переводчика. POKA-YOKE можно перевести как «дуракоустойчивый» или отказоустойчивый.

Инкапсуляция является одной из самых недопонимаемых концепций в объектно-ориентированном программировании (ООП). Похоже на то, что большая часть людей думает, что, имеющая отношение к инкапсуляции, концепция «сокрытия информации», просто означает, что закрытые поля должны быть раскрыты через публичные свойства (или getter\setter-методы в языках, которые не обладают поддержкой свойств).
Вы когда-нибудь задумывались о том, в чём состоит реальное преимущество в том, чтобы унаследовать такой код, как следующий?
private string name;
public string Name
{
    get { return this.name; }
    set { this.name = value; }
}

Для меня такой код выглядит ужасно избыточным (и автоматические свойства – не ответ, это всего лишь уловка компилятора, который, по-прежнему, будет создавать поле). Никакой информации, на самом деле, не сокрыто. Дерик Бейли написал хороший пост о том, почему такой способ инкапсуляции слишком узок, так что я не буду повторять его умозаключения здесь.
Так что же всё-таки означает инкапсуляция?
Основным преимуществом объектной ориентированности является производство связанных кусочков кода (классов), которые решают заданные проблемы раз и навсегда, таким образом, чтобы программисты могли использовать эти классы, ничего не зная об их внутренних деталях реализации.
В этом и состоит вся суть инкапсуляции: предложение (раскрытие) решения проблемы без требования с потребителя полного понимания предметной области.

Вот список того, что обеспечивают хорошо спроектированные классы:
  • Вы не обязаны понимать внутренние детали потока табличных данных (TDS) для того, чтобы использовать ADO.NET в связке с SQL Server.
  • Вы не обязаны понимать внутренние детали рисования на экране для того, чтобы использовать WPF или WinForms.
  • Вы не обязаны понимать внутренние детали рефлексии для того, чтобы использовать DI Container.
  • Вы не обязаны понимать то, как эффективно отсортировать список для того, чтобы отсортировать список, используя .NET-фреймворк.
  • И т.д.

Именно эта характерная черта делает инкапсуляцию такой важной. Класс должен скрывать информацию, которую он инкапсулирует для того, чтобы защитить её от «наивных» пользователей. Википедия говорит следующее:
Сокрытие внутренностей объекта обеспечивает его целостность, предохраняя пользователей от установки внутренних данных компонента в противоречивое или некорректное состояние.

Держите в уме то, что от пользователей не ожидается полное понимание внутренностей реализации класса. Это делает очевидным определение инкапсуляции:
Инкапсуляция это механизм обеспечения отказоустойчивости.

Из этого не следует, что инкапсуляция означает сокрытие сложностей. Всякий раз когда сложность скрыта (как случае с паттерном Provider) время обратной связи увеличивается. Быстрая обратная связь весьма предпочтительна, так что задержек в обратной связи желательно избегать, когда есть возможность.
Инкапсуляция не о сокрытии сложностей, напротив, о том, как раскрыть сложность в отказоустойчивом виде.

В Lean (прим. переводчика: бережливый процесс разработки ПО) это известно как дуракоустойчивость (poka-yoke), я нахожу единственно верным думать об инкапсуляции как о Poka-yoke дизайне: API, которые делают настолько сложным сделать что-либо неправильно, насколько это возможно.
Учитывая то, что компиляция это наиболее дешёвый механизм получения обратной связи, предпочтительно проектировать API так, чтобы код мог только скомпилироваться, если классы использованы правильно. (прим. переводчика. Я думаю, что правильнее сказать: «проектируйте API таким образом (по возможности), чтобы, в случаях его неправильного использования, пользователи получали ошибки компиляции, указывающие на проблему и, если это невозможно, то получали бы уведомление в ходе исполнения программы как можно раньше.)
В серии постов я рассмотрю различные «запахи» проектирования, которые нарушают инкапсуляцию и подготовлю руководство о том, как улучшить спроектированный код для того, чтобы сделать его более безопасным, переходя, таким образом, от «запахов» к благоуханию.
  1. «Запах» проектирования: временная связность.
  2. «Запах» проектирования: одержимость примитивами.
  3. «Запах» кода: автоматические свойства.
  4. «Запах» проектирования: излишний атрибут Required.
  5. «Запах» проектирования: конструктор по умолчанию.

Постскриптум: На границах, приложения не являются объектно-ориентированными.
Автор оригинала: Mark Seemann
Фофанов Илья @EngineerSpock
карма
24,0
рейтинг 0,0
Ответственный программист
Похожие публикации
Самое читаемое Разработка

Комментарии (16)

  • +1
    Спасибо за отличную серию статей!
    API, которые делают настолько сложным сделать что-либо неправильно, насколько это возможно.
    Прекрасная фраза, надо обязательно взять на вооружение.
    • +3
      Спасибо. А вообще я после публикации этой серии статей 5 пунктов в карме уже потерял. Прикольно, да? Что самое интересное — никто, никак не объясняет своего срача в карму.
      • –2
        Я в карму не залазил, но лично мне очень не понравилось что статьи постоянно пропадали-появлялись. И язык чуть-чуть костноват.

        Но темы, поднятые автором, довольно интересны и до сих пор актуальны. Хотя всё это счастье датировано по-моему 2011м годом…
        • –1
          Думаете легко перевести, залить и связать между собой 6 статей? И какая разница каким годом это датировано? Это «вечные» истины.
          • 0
            Ну и зря минусуете. Я хотел сохранить структуру статей. Залил первую — она ведёт на 5 других. Я хотел быстрее залить остальные, чтобы меня не слили за перевод отправной статьи (или одной отрывочной). Делать пришлось быстро. Какой-то косяк получился с одной из статей. Получился дубль. Пришлось одну убирать, потом другую добавлять. Это не жалоба, то, что это проблемно — это факт)))

            Нельзя так ни к кому относиться. Будьте добрее.
            • 0
              Последний абзац в первую очередь относится к вам :)
              Я просто высказал свои догадки, а вы сразу «думаете легко ...». Выглядит грубовато!
  • +4
    Никакой информации, на самом деле, не сокрыто.

    Насколько я понимаю, не сокрыто на текущий момент. А в дальнейшем реализация (поле name) может измениться, а интерфейс (get и set) останется прежним. И те кто так пишут как раз закладывают возможную инкапсуляцию в свой проект.
    • –1
      Дык и что, что когда-то реализация поля может измениться? Всё-равно и перекомпилировать придётся, с сериализацией могут быть проблемы, и после появления какой-либо инкапсулируемой логики наверняка может поменяться способ использования данного поля. Не понимаю, зачем сразу обещать, что там есть какие-то инварианты или логика, если это просто торчащие наружу кишки класса. Ведь по факту, поле vs свойство — это принципиальная разница. И если разработчик сделал AIP, то в 99% случаев только из-за мнимой красоты кода.
      • 0
        По Вашему, в данном случае надо оставить public поле?
        • 0
          Скорее всего да. ИМХО, если есть такие вот тривиальные свойства, значит они хранят просто данные. И, скорее всего, весь объект — это какой-нибудь DTO. В таком случае да, пусть будут public поля. Потому что это реально public поля!

          Если же здесь скрываются свойства, значит под ними есть какая-то логика. Ведь свойства объекта — это либо результат его внутреннего состояния (public Int32 Id { get; private set; } или вычисление значения на лету в get'ере), либо они умеют как-нибудь на него (состояние) влиять (т.е. в set'ере будет код).
          • 0
            Лепят свойства не от хорошей жизни, а потому что некоторые фрейворки ради упрощения работают лишь со свойствами (чтобы дополнительно не реализовывать и работу с полями, ведь в крайнем случае всё можно переделать на свойства, а для пользователя авто-свойство не отличается от поля).

            Те же WPF, NHibernate и т.д.

            Поэтому хорошим тоном считается весь public делать свойствами, чтобы потом не было проблем с какой-то внешней библиотекой.
          • +1
            Вы говорите о том, что СЕЙЧАС, А я о том, что допускаю изменение системы в будущем. И не хочу бегать потом по использующим это поле и уговаривать их перейти на геттер.
            Кроме того, важен принцип — отделение способа хранения данных от способа обращения к ним. Мало ли, может быть я это name захочу потом побитно хранить в десяти разных облачных сервисах. :-)
            При этом снаружи всё тот же геттер. Это инкапсуляция и есть.
    • 0
      Информация (состояние объекта) не сокрыта, а вот его реализация — уже да. Мы уже не можем делать предположений, что, например, после obj.Name = 'name', будет выполняться obj.Name == 'name'
  • 0
    Зря вы разбили. Статьи не такие уж и большие, а сохранить себе их неудобно.
  • 0
    В статье приведены правильные тезисы о хорошо спроектированных классов, тут верно подмечено. А вот про свойство можно прокоментировать, также как коментировали автора оригинала на его блоге: рассуждения, а конкретики нет. Покажите пример хорошей инкапсуляции свойства и плохой. Все-таки это зависит от контекста.
    • 0
      В серии статей есть примеры. Кто не видит — я не виноват :)

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