Время, когда вам нужно рассмотреть класс, который должен быть расширен, - это когда вы делаете реальное планирование на будущее. Позвольте мне привести пример из моей работы.
Я трачу много времени на написание инструментов интерфейса между нашим основным продуктом и внешними системами. Когда мы совершаем новую продажу, один большой компонент - это набор экспортеров, которые предназначены для регулярной работы, которые генерируют файлы данных с подробным описанием событий, которые произошли в тот день. Эти файлы данных затем используются системой клиента.
Это отличная возможность для расширения классов.
У меня есть класс экспорта, который является базовым классом каждого экспортера. Он знает, как подключиться к базе данных, узнать, куда он попал в последний раз, когда он запускался, и создать архивы файлов данных, которые он генерирует. Он также обеспечивает управление файлами свойств, ведение журнала и некоторую простую обработку исключений.
Кроме того, у меня есть другой экспортер для работы с каждым типом данных, возможно, есть активность пользователя, данные транзакций, данные управления денежными средствами и т. Д.
Поверх этого стека я размещаю слой, специфичный для клиента, который реализует структуру файла данных, которая нужна клиенту.
Таким образом, базовый экспортер очень редко меняется. Основные экспортеры типов данных иногда изменяются, но редко, и, как правило, только для обработки изменений схемы базы данных, которые в любом случае следует распространить на всех клиентов. Единственная работа, которую мне приходится выполнять для каждого клиента, - это та часть кода, которая специфична для этого клиента. Идеальный мир!
Итак, структура выглядит так:
Base
Function1
Customer1
Customer2
Function2
...
Моя основная мысль заключается в том, что, создавая код таким образом, я могу использовать наследование в первую очередь для повторного использования кода.
Я должен сказать, что не могу думать ни о какой причине, чтобы пройти три слоя.
Я использовал два слоя много раз, например, чтобы иметь общий Table
класс, который реализует запросы к таблице базы данных, в то время как подклассы Table
реализуют конкретные детали каждой таблицы, используя enum
для определения полей. Предоставление enum
реализации интерфейса, определенного в Table
классе, имеет все виды смысла.
Open/Closed principle
, а неClosed Principle.