Семинар 11 (13.11.2015)
Примеры кода
Исходные коды примеров выложены здесь.
"Крестики-нолики"
Простой пример организации классов по паттерну MVC (Model-View-Controller).
Обратите внимание: несмотря на то, что весь код располагается в одном файле,
в нем есть чётно выделенные компоненты: классы модели, классы контроллера и классы представления.
При этом классы компонент зависят только от определённых раннее классов,
то есть, модель (описанная в самом начале) не зависит от других компонент,
контроллер (описанный ниже) зависит только от модели,
представление (описанное ещё ниже) зависит от модели и от контроллера.
В функции main
инициализируются объекты классов компонент и запускается основной игровой цикл.
Замечание. В реальных программах лучше разбивать классы по отдельным модулям (файлам),
а разные компоненты помещать в разные пространства имён (namespace
).
Примеры с наследованием
01-basic.
Базовый пример с публичным наследование.
Также в примере показана виртуальная перегрузка метода в дочернем классе (whoAmI
).
02-shapes.
Классический пример с геометрическими фигурами.
Абстрактный класс Shape
задаёт общий интерфейс для геометрических фигур:
метод name
для получения имени фигуры и
метод area
для вычисления площади.
У класса Shape
есть три подкласса Circle
, Rectangle
и Triangle
.
03-slicing Пример, демонстрирующий object slicing - "срезку" объектов при присваивании/передаче по значению. То, о чём надо помнить, при выборе метода при передаче объектов (по значению, по указателю или по ссылке).
04-collections Пример, показывающий как хранить коллекцию объектов, которые наследуются от общего класса. Поскольку хранение объектов базового класса не будет работать, как ожидается, из-за "слайсинга", а использование указателей приводит к необходимости вручную освобождать память, в примере показана работа с умными указателями(`std::shared_ptr), которые решают эти две проблемы.
05-dependencies
Разрешение зависимостей с помощью "интерфейсов" и наследования.
Классу View
нужен Player
, чтобы показать текущее состояние игрока.
Но классу Player
в свою очередь нужен View
, чтобы получить ввод от пользователя.
Получается циклическая зависимость между классами двух компонент разных уровней (model и view), что крайне плохо.
Чтобы разрерить эту проблему, нужно объявить интерфейс (абстрактный класс) UserInput
, который предоставляет метод получения ввода от пользователя.
Класс View
реализует этот интерфейс, то есть предлагает конкретную реализацию этого метода,
а класс Player
использует интерфейс вместо конкретной реализации.
Если нарисовать диаграмму зависимостей классов UserInput
,
Player
и View
, то можно обнаружить, что циклическая зависимость изчезла :)