Оба текущих ответа, кажется, только частично достигли цели, и они сосредоточены на примерах, которые затмевают основную идею. Это также не (исключительно) принцип ООП, а принцип разработки программного обеспечения в целом.
В этой фразе «меняется» код. Кристоф говорит о том, что обычно это может меняться, то есть вы часто предвидите это. Цель состоит в том, чтобы защитить себя от будущих изменений в коде. Это тесно связано с программированием на интерфейсе . Тем не менее, Кристоф неверно ограничивать это «деталями реализации». На самом деле, ценность этого совета часто связана с изменениями в требованиях .
Это только косвенно связано с состоянием инкапсуляции, о котором, как мне кажется, думает Дэвид Арно. Этот совет не всегда (но часто делает) предлагает инкапсулирование состояния, и этот совет применим и к неизменным объектам. На самом деле, просто именование констант является (очень простой) формой инкапсуляции того, что меняется.
CandiedOrange явно связывает «что меняется» с «деталями». Это только частично правильно. Я согласен с тем, что любой изменяющийся код в некотором смысле является «деталями», но «детали» могут не меняться (если вы не определите «детали», чтобы сделать это тавтологическим). Могут быть причины для инкапсуляции неизменяемых деталей, но это изложение не одно. Грубо говоря, если бы вы были очень уверены, что «собака», «кошка» и «утка» будут единственными типами, с которыми вам когда-либо придется иметь дело, то это изречение не предполагает рефакторинга, выполняемого CandiedOrange.
Приведя пример CandiedOrange в другом контексте, предположим, что у нас есть процедурный язык, такой как C. Если у меня есть некоторый код, который содержит:
if (pet.type() == dog) {
pet.bark();
} else if (pet.type() == cat) {
pet.meow();
} else if (pet.type() == duck) {
pet.quack()
}
Я могу разумно ожидать, что этот кусок кода изменится в будущем. Я могу «инкапсулировать» его, просто определив новую процедуру:
void speak(pet) {
if (pet.type() == dog) {
pet.bark();
} else if (pet.type() == cat) {
pet.meow();
} else if (pet.type() == duck) {
pet.quack()
}
}
и использование этой новой процедуры вместо блока кода (то есть рефакторинг "метод извлечения"). На этом этапе добавление типа «корова» или чего-либо еще требует обновления speak
процедуры. Конечно, в языке OO вы можете вместо этого использовать динамическую диспетчеризацию, на что ссылается ответ CandiedOrange. Это произойдет естественно, если вы получите доступpet
через интерфейс. Устранение условной логики с помощью динамической диспетчеризации - это ортогональная проблема, которая была частью того, почему я сделал эту процедурную передачу. Я также хочу подчеркнуть, что это не требует особенностей, специфичных для ООП. Даже в ОО-языке инкапсуляция того, что меняется, не обязательно означает, что необходимо создать новый класс или интерфейс.
В качестве более типичного примера (который ближе к ОО, но не совсем), скажем, мы хотим удалить дубликаты из списка. Допустим, мы реализуем его, перебирая список, отслеживая элементы, которые мы видели до сих пор в другом списке, и удаляя любые элементы, которые мы видели. Разумно предположить, что мы можем захотеть изменить то, как мы отслеживаем увиденные объекты, по крайней мере, по причинам производительности. Изречение инкапсулировать то, что меняется, предполагает, что мы должны построить абстрактный тип данных для представления набора видимых элементов. Наш алгоритм теперь определен для этого абстрактного типа данных Set, и если мы решим переключиться на двоичное дерево поиска, наш алгоритм не нуждается в изменении или заботе. На языке ОО мы можем использовать класс или интерфейс для захвата этого абстрактного типа данных. На таком языке, как SML / O '
В качестве примера, основанного на требованиях, скажем, что вам нужно проверить какое-то поле с точки зрения бизнес-логики. Хотя у вас сейчас могут быть конкретные требования, вы сильно подозреваете, что они будут развиваться. Вы можете инкапсулировать текущую логику в ее собственную процедуру / функцию / правило / класс.
Хотя это ортогональная проблема, которая не является частью «инкапсуляции того, что меняется», часто естественно абстрагироваться, то есть параметризоваться, теперь инкапсулированной логикой. Это обычно приводит к более гибкому коду и позволяет изменять логику, заменяя альтернативную реализацию вместо модификации инкапсулированной логики.