Семинар 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, то можно обнаружить, что циклическая зависимость изчезла :)