Один из принципов ООП, с которыми я столкнулся, это: - Инкапсулируйте то, что меняется.
Я понимаю, что буквальное значение фразы, то есть скрыть, что меняется. Тем не менее, я не знаю, как именно это будет способствовать улучшению дизайна. Может кто-нибудь объяснить это на хорошем примере?
design
design-patterns
object-oriented
encapsulation
Харис Гаури
источник
источник
I don't know how exactly would it contribute to a better design
Инкапсуляция деталей - это слабая связь между «моделью» и деталями реализации. Чем менее привязана «модель» к деталям реализации, тем более гибким является решение. И это облегчает его развитие. «Абстрагируйся от деталей».Ответы:
Вы можете написать код, который выглядит следующим образом:
или вы можете написать код, который выглядит следующим образом:
Если то, что меняется, инкапсулировано, вам не нужно об этом беспокоиться. Вы просто беспокоитесь о том, что вам нужно, и все, что вы используете, выясняет, как сделать то, что вам действительно нужно, исходя из того, что изменилось.
Инкапсулируйте то, что меняется, и вам не нужно распространять код, который заботится о том, что изменилось. Вы просто устанавливаете для питомца определенный тип, который знает, как говорить как этот тип, и после этого вы можете забыть, какой тип и просто относиться к нему как к домашнему животному. Вам не нужно спрашивать, какой тип.
Вы можете подумать, что тип инкапсулирован, потому что для доступа к нему требуется геттер. Я не. Геттер на самом деле не заключает в капсулу. Они просто болтаются, когда кто-то нарушает вашу инкапсуляцию. Это хороший декоратор, похожий на аспектно-ориентированный хук, который чаще всего используется в качестве кода отладки. Независимо от того, как вы нарезаете его, вы все равно выставляете текст.
Вы можете посмотреть на этот пример и подумать, что я объединяю полиморфизм и инкапсуляцию. Я не. Я объединяю «что меняется» и «детали».
Тот факт, что ваш питомец - собака, является деталью. Тот, который может отличаться для вас. Тот, который не может. Но, конечно, тот, который может варьироваться от человека к человеку. Если мы не верим, что это программное обеспечение будет когда-либо использоваться любителями собак, разумно будет относиться к собаке как к деталям и инкапсулировать ее. Таким образом, некоторые части системы блаженно не знают о собаке и не будут затронуты, когда мы сольемся с «попугаи - это мы».
Разъедините, отделите и скройте детали от остальной части кода. Не позволяйте знаниям о деталях распространяться по вашей системе, и вы будете хорошо следовать «инкапсуляции того, что меняется».
источник
«Варьируется» здесь означает «может меняться со временем из-за меняющихся требований». Это основной принцип разработки: разделять и изолировать части кода или данных, которые в будущем могут потребоваться изменить отдельно. Если меняется одно требование, в идеале требовать от нас изменения соответствующего кода в одном месте. Но если кодовая база плохо спроектирована, т. Е. Сильно взаимосвязана и логика для требований распределена во многих местах, то изменения будут трудными и с высоким риском возникновения неожиданных последствий.
Скажем, у вас есть приложение, которое использует расчет налога с продаж во многих местах. В случае изменения ставки налога с продаж, что бы вы предпочли:
ставка налога с продаж - это буквально прописная буква везде в приложении, где рассчитывается налог с продаж.
ставка налога с продаж является глобальной константой, которая используется везде в приложении, где рассчитывается налог с продаж.
Существует единственный метод,
calculateSalesTax(product)
который называется единственной, где используется ставка налога с продаж.ставка налога с продаж указана в файле конфигурации или поле базы данных.
Поскольку ставка налога с продаж может изменяться из-за политического решения, не зависящего от других требований, мы предпочитаем изолировать ее в конфигурации, чтобы ее можно было изменить, не затрагивая какой-либо код. Но также возможно, что логика расчета налога с продаж может измениться, например, разные ставки для разных продуктов, поэтому нам также хотелось бы инкапсулировать логику расчета. Глобальная константа может показаться хорошей идеей, но на самом деле она плохая, поскольку она может стимулировать использование налога с продаж в разных местах программы, а не в одном месте.
Теперь рассмотрим другую константу, Pi, которая также используется во многих местах кода. Имеет ли место тот же принцип дизайна? Нет, потому что Пи не собирается меняться. Извлечение его в файл конфигурации или поле базы данных просто создает ненужную сложность (а при прочих равных условиях мы предпочитаем самый простой код). Имеет смысл сделать его глобальной константой, а не жестко кодировать его в нескольких местах, чтобы избежать несоответствий и улучшить читаемость.
Дело в том, что если мы только посмотрим, как программа работает в настоящее время , ставка налога с продаж и Pi эквивалентны, как константы. Только когда мы рассмотрим, что может измениться в будущем , мы понимаем, что мы должны относиться к ним по-разному в дизайне.
Этот принцип на самом деле довольно глубокий, потому что он означает, что вы должны смотреть за пределы того, что кодовая база должна делать сегодня , а также учитывать внешние силы, которые могут привести к ее изменению, и даже понимать различные заинтересованные стороны, стоящие за требованиями.
источник
Оба текущих ответа, кажется, только частично достигли цели, и они сосредоточены на примерах, которые затмевают основную идею. Это также не (исключительно) принцип ООП, а принцип разработки программного обеспечения в целом.
В этой фразе «меняется» код. Кристоф говорит о том, что обычно это может меняться, то есть вы часто предвидите это. Цель состоит в том, чтобы защитить себя от будущих изменений в коде. Это тесно связано с программированием на интерфейсе . Тем не менее, Кристоф неверно ограничивать это «деталями реализации». На самом деле, ценность этого совета часто связана с изменениями в требованиях .
Это только косвенно связано с состоянием инкапсуляции, о котором, как мне кажется, думает Дэвид Арно. Этот совет не всегда (но часто делает) предлагает инкапсулирование состояния, и этот совет применим и к неизменным объектам. На самом деле, просто именование констант является (очень простой) формой инкапсуляции того, что меняется.
CandiedOrange явно связывает «что меняется» с «деталями». Это только частично правильно. Я согласен с тем, что любой изменяющийся код в некотором смысле является «деталями», но «детали» могут не меняться (если вы не определите «детали», чтобы сделать это тавтологическим). Могут быть причины для инкапсуляции неизменяемых деталей, но это изложение не одно. Грубо говоря, если бы вы были очень уверены, что «собака», «кошка» и «утка» будут единственными типами, с которыми вам когда-либо придется иметь дело, то это изречение не предполагает рефакторинга, выполняемого CandiedOrange.
Приведя пример CandiedOrange в другом контексте, предположим, что у нас есть процедурный язык, такой как C. Если у меня есть некоторый код, который содержит:
Я могу разумно ожидать, что этот кусок кода изменится в будущем. Я могу «инкапсулировать» его, просто определив новую процедуру:
и использование этой новой процедуры вместо блока кода (то есть рефакторинг "метод извлечения"). На этом этапе добавление типа «корова» или чего-либо еще требует обновления
speak
процедуры. Конечно, в языке OO вы можете вместо этого использовать динамическую диспетчеризацию, на что ссылается ответ CandiedOrange. Это произойдет естественно, если вы получите доступpet
через интерфейс. Устранение условной логики с помощью динамической диспетчеризации - это ортогональная проблема, которая была частью того, почему я сделал эту процедурную передачу. Я также хочу подчеркнуть, что это не требует особенностей, специфичных для ООП. Даже в ОО-языке инкапсуляция того, что меняется, не обязательно означает, что необходимо создать новый класс или интерфейс.В качестве более типичного примера (который ближе к ОО, но не совсем), скажем, мы хотим удалить дубликаты из списка. Допустим, мы реализуем его, перебирая список, отслеживая элементы, которые мы видели до сих пор в другом списке, и удаляя любые элементы, которые мы видели. Разумно предположить, что мы можем захотеть изменить то, как мы отслеживаем увиденные объекты, по крайней мере, по причинам производительности. Изречение инкапсулировать то, что меняется, предполагает, что мы должны построить абстрактный тип данных для представления набора видимых элементов. Наш алгоритм теперь определен для этого абстрактного типа данных Set, и если мы решим переключиться на двоичное дерево поиска, наш алгоритм не нуждается в изменении или заботе. На языке ОО мы можем использовать класс или интерфейс для захвата этого абстрактного типа данных. На таком языке, как SML / O '
В качестве примера, основанного на требованиях, скажем, что вам нужно проверить какое-то поле с точки зрения бизнес-логики. Хотя у вас сейчас могут быть конкретные требования, вы сильно подозреваете, что они будут развиваться. Вы можете инкапсулировать текущую логику в ее собственную процедуру / функцию / правило / класс.
Хотя это ортогональная проблема, которая не является частью «инкапсуляции того, что меняется», часто естественно абстрагироваться, то есть параметризоваться, теперь инкапсулированной логикой. Это обычно приводит к более гибкому коду и позволяет изменять логику, заменяя альтернативную реализацию вместо модификации инкапсулированной логики.
источник
«Инкапсулировать то, что меняется» относится к сокрытию деталей реализации, которые могут изменяться и развиваться.
Пример:
Например, предположим, что класс
Course
отслеживает,Students
что может зарегистрироваться (). Вы можете реализовать его с помощью aLinkedList
и выставить контейнер, чтобы разрешить итерации по нему:Но это не очень хорошая идея
Если вы инкапсулируете то, что меняется (или, скорее, сказано, что может отличаться), вы оставляете за собой право как для кода, так и для инкапсулированного класса эволюционировать друг друга самостоятельно. Вот почему это важный принцип в ООП.
Дополнительное чтение:
источник