Почему (/ сделал) Бертран Мейер считает, что создание подклассов - единственный способ расширить «закрытый» модуль?

19

В Построении объектно-ориентированного программного обеспечения Мейера (1988) он определяет принцип открытия / закрытия следующим образом:

  • Модуль будет считаться открытым, если он все еще доступен для расширения. Например, должна быть возможность добавить поля к структурам данных, которые он содержит, или новые элементы к набору функций, которые он выполняет.
  • Модуль считается закрытым, если он доступен для использования другими модулями. Это предполагает, что модуль получил четкое, стабильное описание (интерфейс в смысле скрытия информации).

Он продолжает говорить:

Если вы снова откроете модуль, вы также должны снова открыть все его клиенты, чтобы обновить их, так как они полагаются на старую версию. … [Эта проблема] возникает каждый раз, когда модуль должен быть расширен новой функцией или элементом данных, вызывая изменения в прямых и косвенных клиентах. ... При классических подходах к проектированию и программированию нет возможности писать модули, которые бы были как открытыми, так и закрытыми.

Решение этой проблемы Мейера состоит в следующем: никогда не расширять библиотечный модуль, изменяя существующие классы; вместо этого напишите новый модуль, который подклассирует существующие классы, и у которых новые клиенты зависят от этого нового модуля.

Теперь, в 1988 году, я писал игрушечные (процедурные) программы на Turbo Pascal и Blankenship Basic, и мой профессиональный опыт 21-го века связан с JVM, CLR и динамическими языками, поэтому я не знаю, что имел в виду Мейер. по "классическим подходам к дизайну и программированию".

Один конкретный пример Мейера о том, почему клиентские модули должны быть открыты вновь (оператор switch для перечисления, в котором теперь больше членов, требующих большего числа случаев), кажется достаточно разумным, но он почти не оправдывает утверждение, что каждый раз, когда вы добавляете функциональность в библиотеку Модуль, вам необходимо обновить все его клиенты .

Есть ли историческая причина, по которой это утверждение казалось самоочевидным в 1988 году? Скажем, добавление функций или структур данных в статическую библиотеку C изменило макет так, что даже с обратно совместимыми API-интерфейсами клиенты должны были перекомпилироваться? Или Мейер действительно просто говорит о механизме обеспечения обратной совместимости API?

Дэвид Моулз
источник
3
Интересный вопрос! У меня есть ощущение, что ответ будет каким-то образом связан с фундаментальным различием между абстрактными типами данных и объектно-ориентированной абстракцией данных , которые являются двумя доминирующими механизмами абстракции данных в модульном программировании (то, что Бетранд Мейер называет «классическими подходами» ") и объектно-ориентированного программирования (читайте комментарии!) соответственно.
Йорг Миттаг
Это странно. Кажется, вопиюще противоречит реальности (даже в 1988 году). Кроме того, его пропагандируемый подход приведет к бесполезному распространению модулей.
@ dan1111: подход Eiffel к наследованию, включая, но не ограничиваясь этим, подход к множественному наследованию, отличается от C ++, Java, C # и т. д., поэтому неудивительно, что подход отличается. В конце концов, он разработал Eiffel специально для поддержки своих взглядов на ОО.
Йорг Миттаг

Ответы:

18

Насколько я могу судить, на этот вопрос ответил сам Бертран Мейер, и ответ таков: это утверждение не является точным. С классическими подходами к проектированию и программированию действительно может быть способ написания модулей, которые являются открытыми и закрытыми.

Чтобы это выяснить, вам нужно изучить второе издание этой книги (опубликованное девять лет спустя, в 1997 году). Согласно предисловию ко второму изданию , это

не обновление, а результат тщательной переделки. Ни один абзац оригинальной версии не остался нетронутым. (На самом деле вряд ли одна строка.)

В частности, заявление, которое вас смущает, ушло. Там по- прежнему является принцип Открыт-Закрыт глава «§3.3 Пять принципов», и дополнительно подробное обсуждение этой темы в «§ 14.7 Введение в Inheritance» , но заявление от первого издания не существует больше.

Вместо этого основное внимание уделяется тому, как это более удобно и идиоматично в ОО-подходе по сравнению с предыдущими способами.

Благодаря наследованию разработчики ОО могут применять гораздо более инкрементальный подход к разработке программного обеспечения, чем это было возможно в более ранних методах ... (§3.3)

Это двойное требование (открытое и закрытое) выглядит как дилемма, и классические модульные структуры не дают никакой подсказки. Но наследство решает это. Класс закрыт, так как он может быть скомпилирован, сохранен в библиотеке, базовым и использован клиентскими классами. Но он также открыт, так как любой новый класс может использовать его в качестве родительского, добавляя новые функции и перераспределяя унаследованные функции; в этом процессе нет необходимости менять оригинал или беспокоить своих клиентов ... (§14.7)

Поскольку вы также, кажется, задаетесь вопросом о том, что здесь имел в виду «классические подходы» Мейера, вы можете найти объяснение их в §4.7. Традиционные модульные структуры . В этом разделе объясняется, что они означают «библиотеки процедур» и «пакеты» (для последнего автор говорит, что термин взят из Ada и упоминает другие языки, имеющие эту функцию - кластеры в CLU и модули в Modula).

Если вы думаете об этом, ни один из этих подходов изначально не был предназначен для помощи в написании кода, который придерживается принципа открытого-закрытого типа. Это может привести автора к их несколько преждевременной оценке, которая позже была исправлена ​​во втором издании.


Что касается того, что конкретно заставило автора изменить свое мнение об этом утверждении между первым и вторым изданием, я думаю, что можно найти ответ, опять же, в самой книге, а именно в части F: Применение метода в различных языках и средах » . В этой главе автор обсуждает, как объектно-ориентированные методы могут использоваться в старых языках:

Классические языки, такие как Фортран, вообще не являются ОО, но люди, которые все еще должны их использовать ... могут захотеть применить как можно больше идей ОО в рамках ограничений этих старых подходов.

В частности, в этой части Мейер подробно объясняет, как можно было бы реализовать наследование (с некоторыми оговорками и ограничениями, но все же) в C и даже в Fortran.

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

комар
источник
Интересно, и мне определенно придется попытаться достать второе издание, но мне все еще неясно, почему даже не классическая «классическая» библиотека не может добавлять (по крайней мере, некоторые виды) функции, не нарушая ее клиентов.
Дэвид Моулз
@DavidMoles, может , и последняя часть моего ответа объясняет это, и что сам Мейер осознавал это (когда он переделывал для 2-го издания) и даже приводил примеры того, как это можно сделать. «Что касается того, что конкретно заставило автора изменить свое мнение ...» и т. Д.
комнат
Хм. Я не вижу, «версия 2 этой библиотеки, которая заменяет версию 1 и обратно совместима с ней, добавляет следующие функции…» как «наследование», за исключением самого широкого возможного концептуального способа.
Дэвид Моулз
(Для меня наследование подразумевает, что версия 1 все еще существует и вызывается версией 2.)
Дэвид Моулз
Замена @DavidMoles версией 2 (как, например, изменение исходного кода и перекомпиляция ) не будет квалифицироваться как «закрыта для модификации», вы можете просто проверить это в статье в Википедии : «сущность может позволить расширять свое поведение без изменения исходного кода ... "
комнат