Для чего нужны интерфейсы на примере паттерна "Стратегия"

Будем говорить контексте игры, что бы проще было оперировать сущностями.

Например это стратегия в реальном времени, где есть юниты, скажем лучник и мечник. И все у них вроде бы одинаковое, двигаются одинаково, оба имеют здоровье и броню, но у них есть одно существенное отличие - атака, она у них отличается в корни.

Первое, что приходит в голову - сделать класс Warrior, где описать общие свойства вроде здоровья, скорости движения, брони и сделать пустой (abstract) метод Attack(), который мы будем перегружать в подклассах Archer и Swordsman.

И вроде бы все у нас хорошо, код не дублируются, методы атаки реализуются по разному в каждом подклассе, все ок. Но сама система становится довольно жесткой, нет никакой гибкости, и вроде бы она не нужна, но что если юнит будет не статичный в игре? Что если у него будет прокачка? И например начнет он голыми руками, потом перейдет к мечу, потом к луку, потом к гранотомету, да и передвигаться начнет уже не пешком, а на джетпаке в экзоскелете?

Мы либо пишем тонну кода в одном классе и реализуем так называемый синглтон? Либо идем по пути абстракции - интерфейсов и реализуем паттерн стратегия?

Например в суперклассе Warrior мы указываем, что метод Attack() является интерфейсом, при этом мы конечно же делаем интерфейс iAttack(), который содержит только один метод, и создаем несколько классов, которые реализуют этот интерфейс, пусть AttackWithHands, AttackWithSword, AttackWithBow.

При инициализации объекта Warrior, мы передаем нужный объект в качестве метода атаки. Что это дает? Так как атака является интерфейсом, то есть у нее появляется своего рода тип данных для целого класса (если классы реализуют один интерфейс. то значит они могут быть взаимозаменимы), мы можем динамически менять у любого объекта warrior его атаку, без необходимости его пересоздавать.

По сути в итоге можно создать полностью абстрактную сущность, где будет минимум каких-то статичных показателей (которые действительно не будут меняться), и максимум интерфейсов, которые будут реализовываться в отдельных классах.

Тем самым мы будем оперировать на глобальном уровне абстрактными методами и нам совершенно не важно, как они реализованы внутри, достаточно знать, что они совместимы.

Интерфейс нужен для связывания и совместимости разных классов, своего рода тип данных, который связывает плоскость классов, которые вроде бы похоже, но в тоже время имеют отличие.

Это и есть полиморфизм, вернее одна из его сторон.

По сути это и есть паттерн стратегия, смысл которого состоит в том, что мы определяем некий спектр (семейство) классов с общим смысловым поведением и реализуем их через один интерфейс, который обеспечивает их взаимозаменяемость прямо во время выполнения программы.


comments powered by Disqus