Comments 71
Подлинное назначение инкапсуляции — собрать в одном месте знания, относящиеся к устройству некой сущности, правилам обращения и операциям с ней.
Откуда вы берете "подлинность" этого назначения, и почему вы считаете, что определение, данное, скажем, в вики — менее "подлинное"?
Для начала встречный вопрос: вы согласны, что размазывание деталей реализации по всему коду — это плохо?
Второй вопрос: вы согласны, что значение термина — это вопрос договорённости?
Теперь мой ответ.
Я считаю, что определение в вики хуже, потому что оно не ведёт прямиком к полезному эффекту сокрытия реализации. И даже сокрытие данных, которое могло бы способствовать сокрытию реализации, в классическом понимании инкапсуляции не подразумевается.
Можно объединить поля данных и некоторые процедуры, к ним относящиеся, в одном классе, но это не гарантирует, что знания об устройстве объекта больше нигде не всплывут. Гарантию может дать только сам программист, если будет твёрдо придерживаться правила сосредотачивать все знания об устройстве объекта в одном месте.
Фактически, определение из вики — это частный случай моего определения: да, инкапсуляцию в смысле сокрытия реализации можно осуществить в том числе и при помощи инкапсуляции данных и методов в один класс.
P.S.
Определения терминов из мира программирования формируются исходя из работ авторитетных товарищей из этого мира программирования. Между тем, товарищи Роберт Мартин и Мартин Фаулер не намного умнее нас с вами, просто они пишут книги, а мы — нет. Поэтому я предлагаю нам с вами тоже сделать свой вклад в улучшение мира программирования. Например, путём корректировки некоторых не очень хороших определений терминов на определения, которые приведут в конечном итоге к созданию более качественного кода каждым программистом.
вы согласны, что размазывание деталей реализации по всему коду — это плохо?
Да (обычно).
вы согласны, что значение термина — это вопрос договорённости?
Да.
Я считаю, что определение в вики хуже, потому что оно не ведёт прямиком к полезному эффекту сокрытия реализации.
Это очень странно, учитывая, что там явно написано про "information-hiding mechanism".
Впрочем, ваше определение тоже не ведет к полезному эффекту сокрытия реализации.
Более того, вы приравниваете "сокрытие реализации " к "неразмазыванию функциональности", хотя это не одно и то же.
Гарантию может дать только сам программист, если будет твёрдо придерживаться правила сосредотачивать все знания об устройстве объекта в одном месте.
И это правило не обязательно будет инкапсуляцией, вот что важно.
Фактически, определение из вики — это частный случай моего определения
Нет. Ваше определение слишком размыто (до невыполнимости) в части information hiding.
Определения терминов из мира программирования формируются исходя из работ авторитетных товарищей из этого мира программирования.
И из консенсуса в сообществе.
Между тем, товарищи Роберт Мартин и Мартин Фаулер не намного умнее нас с вами,
У вас нет оснований для этого утверждения.
Например, путём корректировки некоторых не очень хороших определений терминов на определения, которые приведут в конечном итоге к созданию более качественного кода каждым программистом.
Для этого надо сначала определиться, какое определение "более хорошее". Я вот ваше "более хорошим" не считаю.
А зачем брать существующий термин с точным значением и пытаться его поменять? Что мешает придумать новый термин с нужным вам значением и использовать его?
Термин уж больно хороший: заворачивание чего-то во что-то. Как раз то, о чём я и говорю.
Может все-таки стоит брать определение из соответствующей предметной области?
Инкапсуляция (англ. encapsulation, от лат. in capsula) — в информатике упаковка данных и функций в единый компонент.
Оно слишком водянистое, недостаточно точное и вообще не раскрывает сути, которая используется при назывании четырех китов ООП. Ну вот, к примеру:
То есть если упаковать все-все данные и функции в единый компонент — это будет инкапсуляция? Если это данные из разных предметных областей? То есть если я в одном компоненте опишу деньги на счете пользователя и количество сообщение на форуме, то, согласно этому определению, это будет инкапсуляция, но по духу, по моему мнению, это как раз будет нарушение инкапсуляции.
Чтобы этот термин был более корректным — ему необходимо как минимум добавить описание данных и функций, которые могут объединятся в одном компоненте. То есть
Инкапсуляция (англ. encapsulation, от лат. in capsula) — в информатике упаковка данных и функций (которые обладают определенным свойством) в единый компонент.
Я понимаю, что с моими замечаниями инкапсуляция уже начинает набирать признаков SRP, но он близок по сути и к принципу сокрытия и к принципу абстракции.
Там ниже есть "подробности":
языковая конструкция, позволяющая связать данные с методами, предназначенными для обработки этих данных.
То есть, более правильное определение будет такое:
Инкапсуляция (англ. encapsulation, от лат. in capsula) — в информатике упаковка данных и функций, предназначенных для обработки этих данных в единый компонент.
Так? Всё дело в том, чтобы можно было положить данные и функции для них в один класс/неймспейс/итд?
Теперь мне это определение кажется слишком мелким в качестве основополагающего принципа ООП. Ну то есть, когда мы говорим об определениях таких терминов, то я смотрю на ООП как на теоретическую научную концепцию, сферический шар в вакууме. А тут такая грубая привязка на механику языков.
Не знаю, смог ли я объяснить, я чувствую, что аргументы мутные. Просто это из разряда: «чтобы язык был ООПшный — там должно быть ключевое слово class». Это не подходит для теоретиков, которые придумали принцип подстановки Барбары Лисков, это подходит для ребят, которые спорят за пивом в баре.
Теперь мне это определение кажется слишком мелким в качестве основополагающего принципа ООП.
А я (лично) и не считаю, что инкапсуляция — это "основополагающий принцип ООП". Для меня это всего лишь один из принципов, которые делают разработку легче/более поддерживаемой. И как раз привязки к ООП в понимании термина "инкапсуляция" я пытаюсь избегать.
PS Ну и отдельно я, на самом деле, пытаюсь избегать русской википедии, потому что англоязычные определения терминов часто лучше.
А в контексте современной Computer Science инкапсуляция является одним из основных принципов ООП
… но при этом не уникальна для ООП, про что явно написано в английской википедии.
Ну и на самом деле, тут есть очень интересный вопрос: а что такое "основной принцип"? Это принцип, без которого (ООП) не будет? Или принцип, который если есть, то ООП? Или что-то третье?
Вообще, чем дальше, тем интереснее найти, кто же конкретно ввел этот термин, и сказал, что "он основополагающий".
Может все-таки стоит брать определение из соответствующей предметной области?
Я просто описал, почему считаю определение из предметной области сомнительным и согласен с идеей, что неплохо было бы над нам подумать, а вы, похоже, со мной согласны (как минимум, отчасти).
И как раз привязки к ООП в понимании термина «инкапсуляция» я пытаюсь избегать.
Да, я согласен. Мне нравится пример, который ниже привел автор статьи:
Правильно объединять деньги их округляя. То есть мы везде пишем:
const sum = round(amount1 + amount2);
Но это уже детали реализации, которые лишние каждый раз. Потому мы вводим функцию:
function add_money (amount1, amount2) {
return round(amount1 + amount2);
}
И этим инкапсулируем логику. Такое видение инкапсуляции мне кажется намного лучше, чем то, что описано в википедии:
1. Это реальное влияние на поддержку кода
2. Оно не зависит от парадигм — подходит и под ООП и под ФП и под ПП.
3. Оно никак не зависит от механик языка. То есть нам не нужны специфические языковые конструкции для соблюдения этого принципа (меня страшно выбешивает, когда люди считают модификатор private как-то связанным с инкапсуляцией)
4. Я верю, что это близко к аксиоме, что такой подход к коду полезен, в отличии от холиварного подхода с «методами и свойствами в одном классе».
С таким видением инкапсуляции этот принцип становится реально теоретическим и полезным, но, увы, я недостаточно образован, чтобы это видение формализовать. Может вы сможете помочь?
Вот разобрать причины перехода к anemic object было бы очень интересно.
В значении:
(Субъектность — свойство индивида быть субъектом активности.)
Если это объект, состояние которого определяется ещё и приватными свойствами, да ещё и с бизнес-логикой, то вы получите архитектурный косяк. В лучшем случае просто нарушение Single Responsibility, когда одна и та же сущность используется для двух совершенно разных задач. В худшем — проблему синхронизации состояний двух разных объектов.
>Если это объект, состояние которого определяется ещё и приватными свойствами, да ещё и с бизнес-логикой, то вы получите архитектурный косяк.
Если он сам себя сериализует/десериализует, теряя по дороге приватные свойства — то это его личные внутренние проблемы, разве нет? Ну да, согласен, архитектурно это косяк. Вполне себе очевидный — не надо было пихать бизнес логику в те объекты, которые могут путешествовать по сервисам или тем более передаваться по сети.
Но из этого совершенно не следует, что все объекты должны быть только такими.
Только вот IRL люди отказываются от инкапсуляции и переходят к anemic object.
Когда вы противопоставляете инкапсуляцию анемичным объектам, вы имеете в виду только одно из (как минимум) двух возможных значений понятия "инкапсуляция".
Например, можно переходить к анемичным объектам и все равно не отказываться от инкапсуляции.
Да, мне тоже любопытно понять, почему anemic набирает популярность. Думаю, у её приверженцев есть достаточно сильные аргументы.
Раз есть тенденция к популяризации anemic — значит, для кого-то это имеет смысл. Значит, инкапсуляция в смысле википедии не так уж и хороша, раз ей пренебрегают.
Моё ИМХО — это просто другой подход. Не лучше и не хуже, продуктивность разработки и сопровождения не слишком отличается в обоих случаях. Тенденции в ИТ подвержены моде точно так же, как и в одежде. Выкатил кто-то известный новый гайдлайн по разработке — и падкий до новых блестящих штук народ массово кидается юзать. Тем более что новые технологии и клиентам продаются лучше зачастую просто потому, что новые.
Да, мне тоже любопытно понять, почему anemic набирает популярность. Думаю, у её приверженцев есть достаточно сильные аргументы.
Мое мнение, anemic модели просто более есстественны, это всё же данные, например, у реального документа нет поведение.
+ это путь наименшего сопротивления, когда отсувствует четких правил по архитектуры.
у реального документа нет поведение.
… а у кого "реального" есть поведение?
Думаю неудачно написал.
Если не проектируем живых организмов, поведение думаю нет, т.е. в большинстве случаев не проектируем ничего с поведением.
Ой ли?
"получив заказ от покупателя, оператор проверяет наличие всех позиций на складе, а затем отправляет покупателю счет на оплату"
Это разве не поведение?
Они-то живые организмы. Но вот дальше начинаются две маленьких проблемы.
- Мы их не проектируем (нам совершенно не важно, что оператор — живой организм, мы его устраняем из процесса, а покупатель — это внешний агент)
- Не важно, проектируем мы их или нет, подавляющее большинство систем, обслуживающих бизнес, работает именно с такого рода сценариями, следовательно, утверждение "в большинстве случаев не проектируем ничего с поведением" неверно.
например, у реального документа нет поведение.
Смотря где вы проведёте границу ваших абстракций. Можете рассматривать документ как бумажку. А можете рассматривать документ как бумажку и связанные с ней правила валидации данных. А можете рассматривать как бумажку, правила валидации и соответствующий бизнес-процесс. Не существует никакой самоцели делать модель данных в точности соответствующей тому, что пишется в бумажных аналогах. Делайте так, как удобнее.
Смотря где вы проведёте границу ваших абстракций.
Согласен.
А можете рассматривать как бумажку, правила валидации и соответствующий бизнес-процесс.
Но даже в этом случае вроде нет поведения, т.е. бумажка + правила о том что с ним делать.
Ошибся, поведение есть
см. https://habr.com/post/435900/#comment_19609626
А можете рассматривать как бумажку, правила валидации и соответствующий бизнес-процесс
Соответствующий бизнес-процесс обычно кроме документа включает и другие сущности. Почему его надо помещать в документ, а не в любую другую?
Не существует никакой самоцели
Самоцели такой нет, но разговор-то был о том, почему так делают. Цель же не только в том, чтобы сделать модель на текущий момент, а чтобы изменения было проще вносить. Можно сделать не так, как в бумажных аналогах, но может получиться так, что бумажные аналоги просто передадут в другой отдел, а в системе придется половину классов переписывать.
Соответствующий бизнес-процесс обычно кроме документа включает и другие сущности. Почему его надо помещать в документ, а не в любую другую?
Не «надо», а «можно». Проектируя архитектуру приложения, у вас стоит задача в том, чтобы она
а) эффективно решала поставленную задачу
б) была понятной
в) была сопровождаемой
г) укладывалась в бюджет разработки
Всё остальное вторично. Более того, даже всякие DRY, SOLID и иже с ними — это лишь методы достижения этих целей, которые в общем случае помогают их достичь, но бывают кейсы, когда их нужно нарушать.
Почему бизнес-процесс может быть помещён в документ? Например, в вашей системе документооборота центральной сущностью является документ, и не существует бизнес-процессов, не построенных вокруг какого-то документа. Они могут порождать другие документы, они связаны с сущностями участников и чего-либо ещё, но всегда вертятся вокруг одного документа. Здесь решение агрегировать их в документ будет вполне адекватным среди других решений.
Но факт остается фактом — документ сам себя не заполняет, его заполняет кто-то извне, и во всех процессах это так. Значит представление документа как анемичной модели точнее моделирует реальные процессы. Автоматизация убирает участие человека, но выполняемая им функция остается. Она может быть отнесена к объектам типа "Отдел" или вынесена в техническую сущность "сервис", но не к самому документу. У нее даже может быть свое состояние, которое не зависит от состояния документов.
Значит представление документа как анемичной модели точнее моделирует реальные процессы.
Да, но зачем точно моделировать реальные процессы? Задача автоматизации не состоит в том, чтобы их точно моделировать. В каких-то частных случаях это может быть нужно. В общем — нет. У вас есть задача, допустим, реализовать какой-то процесс согласования документа, и как оно внутри реализовано, для пользователей системы вообще чёрный ящик. Поэтому насколько ваша архитектурная модель повторяет их старые бумажки и телодвижения, они не оценят. Вам же для самого себя надо тоже писать модель не так, чтобы было больше похоже на бумажные процессы, а так, чтобы она
а) эффективно решала поставленную задачу
б) была понятной
в) была сопровождаемой
г) укладывалась в бюджет разработки
Ну так анемичная модель это все и дает. А точная модель дает то, что не надо искать, где она отличается от реальной бизнес-логики и как это менять при реальных изменениях.
Ну так анемичная модель это все и дает.
Да ничего она сама по себе не даёт, если честно. Всё в руках и голове разработчика. Испоганить анемичный объект с сервисом так же легко, как и старый добрый толстый объект.
Я не раз видел, как делают модель справочника, например, «категории клиентов», и к ней сервис, который позволяет CRUDить эти категории. И так — ко всем аналогичным справочникам. Просто потому, что в книжке где-то было сказано, что делать надо было так. В итоге получается приложение с кучей классов-прослоек, который кошмарен в плане сопровождения.
А в чем сложности с сопровождением?
Вы имеете в виду, лучше в один справочник все поместить? А как быть если потом у категорий клиентов появятся дополнительные атрибуты?
Вы имеете в виду, лучше в один справочник все поместить?
Нет, в данном случае такого сервиса вообще быть не должно. Только контроллер с формой редактирования/добавления. А операции вида «назначить категорию клиенту» относятся к сущности клиента и должны быть в сервисе управления клиентами.
А как быть если потом у категорий клиентов появятся дополнительные атрибуты?
Есть очень простое правило в разработке, которое, как ни странно, чаще соблюдают джуниоры и намного реже опытные разработчики: пишите код с заделом на будущее, если вы точно знаете, что ваше приложение будет масштабироваться, и как оно будет масштабироваться. И не пишите код с заделом на будущее, если вы не знаете, будет ли оно масштабироваться в будущем. Потому что это означает в 90% случаев, что оно масштабироваться не будет. Намного эффективнее переписать заново узкие места в 10% случаев, чем потратить ненужные усилия в 90%, закладывая в архитектуру то, что вам никогда не понадобится.
Только контроллер с формой редактирования/добавления.
Редактирования/добавления категорий клиентов? Ну так если не будет отдельного сервиса, код для CRUD будет в контроллере, контроллер и будет таким сервисом. Просто зачем писать его в контроллере, у него другая ответственность.
И не пишите код с заделом на будущее, если вы не знаете, будет ли оно масштабироваться в будущем.
Я все-таки не совсем понимаю, что вы имеете в виду. В контексте данного примера этот совет выглядит так, что вы предлагаете хранить категории клиентов и категории заказов в одной таблице, потому что у них одинаковые поля "id, name". Потому что если хранить в разных и иметь соответствующие классы в коде, то проблемы нет. Но я бы не назвал этот код "с заделом на будущее", так как у него есть преимущества сразу — ссылочная целостность в базе, типизация в коде.
Anemic не исключает сокрытия реализации = инкапсуляции
Как раз сокрытие реализации оно исключает. Скорее оно не исключает методы, даже по определению:
Anemic domain model is the use of a software domain model where the domain objects contain little or no business logic (validations, calculations, business rules etc.).
То есть такой подход вполне себе анемичен, открытые данные, с которыми можно работать извне и мало бизнес-логики:
class Box
{
public int Height { get; set; }
public int Width { get; set; }
public int area()
{
return Height * Width;
}
}
Возможно, отделение бизнес-логики от сущностей, которые соответствуют строкам в базе данных, в каких-то случаях лучше моделирует реальные бизнес-процессы. Заявка сама себя не заполняет, заказ сам себя не оплачивает, есть сторонний функциональный элемент (сервис), который одинаково работает со многими сущностями, может даже с несколькими сразу.
Пример: работа с денежными величинами. Не секрет, что во многих e-commerce системах денежные величины реализованы в виде чисел с плавающей запятой.
Не слишком правильный пример, т.к. в финансовых системах чаще используются специальные «денежные» типы без побочных эффектов точности. А в банковских так вообще вообще очень распространено использование целых чисел, представляя сумму операции изначально в копейках/центах
Не секрет для кого? Можно хотя бы пару примеров таких систем — их ведь много, правда же, это же не сложно показать парочку?
Но да инкапсуляциям как и почти все что связанно с хорошим тоном написания кода появилось давно — в годах этак 60, и соглашусь что это не каждый знает. Но! Скажем так — все придумали пол столетия назад, но пользоваться начали недавно, да и многие умные мысли из того времени не используются.
Да и какая разница когда оно появилось — важнее когда оно дошло до сообщества.
Может и не хорошо, но что давно известной — это факт.
«Предположим, у тебя на счету в банке 8000 долларов, которые приносят тебе 8 процентов годовых. Раз в неделю банк пересчитывает твой счет. То есть в конце первой недели банк умножит сумму счета на 0,0015384 и прибавит результат к сумме счета. Твое состояние увеличится на 12,30 доллара. Правильно? Проверь на калькуляторе.Я подсчитал – результат был верен.
– Ровно двенадцать долларов и тридцать сантимов, – подтвердил я.
– А вот и нет, – решительно возразил он. – Прибыль-то составила 12,3072, не так ли?
– Да, но как прибавишь к счету семьдесят две сотые сантима?
– Да, это непросто, потому что банковские счета знают только вторую цифру после запятой.
Гарри Гаррисон „Рождение Стальной Крысы“.
))))))))))
А деньги нормальные люди хранят целыми числами в копейках или в Decimal-типах, так что мимо.
Редко кто осознанно размазывает знания о функционале по коду, думаю чаще это происходит из-за неверно выбранной абстракции, в таком случае поздно инкапсулировать — надо рефакторить код.
Ну почему же поздно? Рефакторинг в данной ситуации и будет состоять в извлечении метода/класса, то есть, в «моей терминологии», в инкапсуляции кусочков кода куда-то там.
А деньги нормальные люди хранят целыми числами в копейках или в Decimal-типах, так что мимо.
А ненормальные обрабатывают во float-ах, и тоже неплохо зарабатывают.
Главное назначение инкапсуляции — это сокрытие сложности, а вовсе не компоновка схожего функционала. Компоновка(как противоположность размазыванию) — это полезный, но побочный эффект инкапсуляции.
Подлинное назначение инкапсуляции — собрать в одном месте знания, относящиеся к устройству некой сущности, правилам обращения и операциям с ней.
Собрать в одном месте != спрятать.
Далее вы говорите и о том, что бы спрятать:
Инкапсуляция в данном случае — собрать (спрятать) в одном месте знание о том...
но подлинным назначением инкапсуляции это не называете.
Например (условно), раньше в коде я после каждой операции сложения денег выполнял округление, чтобы избежать побочных эффектов точности:
sum = round(money_a + money_b)
И таких мест по коду было очень много. В каждой такой точке содержался кусочек знания о том, что реализация денег в этом коде хреновая (в виде float), деньги нужно постоянно округлять.
Потом я заменяю сложение с округлением на специальный вызов, который сам умеет правильно складывать деньги с нужным округлением:
sum = add_money(money_a, money_b)
Теперь вы читаете add_money, и не видите никаких round. Я спрятал round? — спрятал.
Таким вот нехитрым способом я прячу от глаз подальше подробности того, как нужно работать с деньгами в этом коде: просто вызывай add_money — и всё всегда будет хорошо, и никаких проблем с точностью.
А что я сделал? Я инкапсулировал код, который выполняет нужные операции, в отдельный модуль, в отдельную функцию. Инкапсулировал? — да. Спрятал? — тоже да.
class A {
public round(...);
public add_money(...)
}
Инкапсуляция:
class B {
private round(...);
public add_money(...)
}
Инкапсуляция:
Не, это сокрытие. Почему вы считаете наличие ключевого слова private хоть сколько то важным для инкапсуляции?
Главное назначение инкапсуляции — это сокрытие сложности, а вовсе не компоновка схожего функционала.Я вот тоже хотел бы видеть инкапсуляцию именно в таком значении, но больше похоже, что вы заходите в сторону термина «абстракция».
Посмотрел предущую статью про мускулы. Похоже на тенденцию, людям нравится… Мини-блог? Бессвязные мысли вслух?
Может написать статью «Солнце светит» или «Ветер дует»? Или может обобщить «Ветер дует не так, как солнце светит»? /confused
Инкапсулируй это