- Основы ООП: абстракция
- Основы ООП: инкапусляция
- Основы ООП: наследование
- Основы ООП: полиморфизм
- Основы ООП: композиция (дизайн)
- Определение класса
- Права доступа
- Конструкторы: по умолчанию, параметрический, копирования
- Деструкторы
- Ссылка на себя: this
- Наследование
- Динамическое создание объектов: new и delete
- Указатели
- Виртуальные методы (virtual и override)
- Статические члены
- Вложенные типы, пространства имен и оператор контекста '::'
- Разнесение объявления и определения класса
- Абстрактные классы и интерфейсы
- Перегрузка операторов
- Приведение объектных типов: dynamic_cast и указатель void *
- Указатели, ссылки и const
- Управление наследованием: final и delete
Основы ООП: композиция (дизайн)
При проектировании программ с применением ООП встает задача нахождения оптимального (по некоторым заданным характеристикам) разбиения на классы и отношений между ними. В русском языке для обозначения этого лучше всего подошел бы термин композиция (как составление, соединение частей в единое целое). К сожалению, данный термин сильно перегружен в английской терминологии, и используется с разными значениями, включая и один из частных случаев "составления" классов. Это отступление необходимо, потому что при чтении другой компьютерной литературы Вы можете найти различные толкования термина "композиции": как в обобщенном, так и более узком смысле. Мы постараемся изложить данную концепцию, конкретизируя смысл терминов в каждом случае (когда подразумевается общий "дизайн/ проектирование" программного интерфейса, а когда — "композиционное агрегирование").
Итак, класс, как мы знаем, состоит из полей (свойств) и методов. Свойства, в свою очередь, могут быть описаны пользовательскими типами, то есть представлять собой объекты другого класса. При этом существует несколько способов логического соединения этих объектов:
- Композиция (полное включение или композиционное агрегирование) объектов-полей в объект-владелец. Связь таких объектов описывается отношением "целое-часть", причем часть не может существовать вне целого. Говорят, что объект-владелец "имеет" ("has a") объект-свойство, а объект-свойство "является частью" ("part of") объекта-владельца. Владелец создает и уничтожает свои части. Удаление владельца приводит к удалению всех его частей; владелец не может существовать без частей.
- Агрегирование объектов-полей объектом-владельцем представляет собой более "мягкое" включение. Хотя отношение также описывается как "целое-часть", владелец лишь содержит ссылки на части, которые могут назначаться, меняться и существовать в отрыве от целого. Более того, одна часть может использоваться в нескольких "владельцах".
- Ассоциация, то есть одно- или двухсторонняя связь независимых объектов, имеющая произвольный прикладной смысл. Говорят, что один объект "использует" другой.
Не следует забывать еще один тип отношений: "является" ("is a"), рассмотренный ранее в разделе про наследование.
В качестве примера полного включения можно привести машину и её двигатель. Здесь под машиной понимается полноценное средство передвижения. Без мотора это не так. И конкретный двигатель принадлежит в каждый момент времени только одной машине. Ситуации, когда двигателя в машине еще нет (на заводе) или уже нет (в автомастерской), эквивалентны тому, что мы сломали исходный код программы.
Примером агрегирования является состав групп студентов для занятий по определенным курсам: группа для каждого курса включает несколько студентов, причем любой из них может относиться и к другим группам (если слушает несколько предметов). Группа "имеет" слушателей. Выход студента из состава группы, не сказывается на учебном процессе группы (остальные продолжают учиться).
Наконец, для демонстрации идеи ассоциации рассмотрим компьютер и принтер. Можно сказать, что компьютер использует принтер для печати. Принтер может быть включен или выключен по необходимости, причем один и тот же принтер можно использовать с разных компьютеров. Все компьютеры и принтеры существуют независимо друг от друга, но могут использоваться совместно.
Что касается характеристик, которыми принято руководствоваться при проектировании классов, то к наиболее известным относятся:
- DRY (Don't repeat yourself) — "не дублируй код": вместо этого выноси общие части в родительские (по возможности, абстрактные) классы;
- SRP (Single Responsibility Principle) — "разделяй сферы ответственности": один класс должен выполнять одну задачу, а если это не так — нужно его раздробить на более мелкие;
- OCP (Open-Closed Principle) — "пиши код открытым для расширения, но закрытым для модификации": если в классе X "зашито" несколько вариантов расчета и могут появиться новые, сделай базовый (абстрактный) класс для отдельного расчета и на его основе создавай специфические варианты ("расширение" функционала), подключаемые к классу X без его модификации.
Это лишь малая часть лучших практик проектирования классов. После освоения основ ООП, ограниченных рамками данной книги, может быть полезно познакомиться с другими специализированными источниками информации по данной теме, поскольку в них содержатся готовые решения для декомпозиции на объекты многих типичных ситуаций.