Стандарты программирования на С++. 101 правило и рекомендация
Шрифт:
• Если класс
Словом, лучше добавлять новую функциональность посредством новых свободных (не являющихся членами) функций (см. рекомендацию 44). Чтобы избежать проблем поиска имен, убедитесь, что вы поместили функции в то же пространство имен, что и тип, для расширения функциональности которого они
Но что если класс
И наконец, наследование класса с открытым невиртуальным деструктором рискует получить эффект неопределенного поведения при удалении указателя на объект типа
Примеры
Пример 1. Композиция вместо открытого или закрытого наследования. Что делать, если вам нужен тип
Конечно, писать транзитные функции для всех функций-членов, которые вы хотите сохранить, — занятие утомительное, но зато такая реализация существенно лучше и безопаснее, чем использование открытого или закрытого наследования.
Пример 2.
[Dewhurst03] §70, §93 • [Meyers97] §33 • [Stroustrup00] §24.2-3, §25.2
36. Предпочитайте предоставление абстрактных интерфейсов
Вы любите абстракционизм? Абстрактные интерфейсы помогают вам сосредоточиться на проблемах правильного абстрагирования, не вдаваясь в детали реализации или управления состояниями. Предпочтительно проектировать иерархии, реализующие абстрактные интерфейсы, которые моделируют абстрактные концепции.
Предпочитайте определять абстрактные интерфейсы и выполнять наследование от них. Абстрактный интерфейс представляет собой абстрактный класс, составленный полностью из (чисто) виртуальных функций и не обладающий состояниями (членами-данными), причем обычно без реализации функций-членов. Заметим, что отсутствие состояний в абстрактном интерфейсе упрощает дизайн всей иерархии (см. соответствующие примеры в [Meyers96]).
Желательно следовать принципу инверсии зависимостей (Dependency Inversion Principle, DIP; см. [Martin96a] и [Martin00]). Данный принцип состоит в следующем.
• Высокоуровневые модули не должны зависеть от низкоуровневых. И те, и другие должны зависеть от абстракций.
• Абстракции не должны зависеть от деталей; вместо этого детали должны зависеть от абстракций.
Из DIP следует, что корнями иерархий должны быть абстрактные классы, в то время как конкретные классы в этой роли выступать не должны (см. рекомендацию 35). Абстрактные базовые классы должны беспокоиться об определении функциональности, но не о ее реализации.
Принцип инверсии зависимостей имеет три фундаментальных преимущества при проектировании.
• Повышение надежности. Менее стабильные части системы (реализации) зависят от более стабильных частей (абстракций). Надежный дизайн — тот, в котором воздействие изменений ограничено. При плохом проектировании небольшие изменения в одном месте расходятся кругами по всему проекту и оказывают влияние на самые неожиданные части системы. Именно это происходит, когда проект строится на конкретных базовых классах.
• Повышение гибкости. Дизайн, основанный на абстрактных интерфейсах, в общем случае более гибок. Если абстракции корректно смоделированы, то при появлении новых требований легко разработать новые реализации. И напротив, дизайн, зависящий от многих конкретных деталей, оказывается более жестким в том смысле, что новые требования приводят к существенным изменениям в ядре системы.
• Хорошая модульность. Дизайн, опирающийся на абстракции, обладает хорошей модульностью благодаря простоте зависимостей: высокоизменяемые части зависят от стабильных частей, но не наоборот. Дизайн же, в котором интерфейсы перемешаны с деталями реализации, применить в качестве отдельного модуля в другой системе оказывается очень сложно.
Закон Второго Шанса гласит: "Самая важная вещь — интерфейс. Все остальное можно подправить и позже, но если интерфейс разработан неверно, то может оказаться так, что у вас уже не будет второго шанса его исправить" [Sutter04].