Java. Factory Method Pattern in Game Server

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Game Server
Game Server

Фабричный метод - это порождающий шаблон проектирования, который предоставляет интерфейс для создания объектов в родительском классе, но позволяет подклассам изменять тип создаваемых объектов.

Проблема

Представьте, что вы создаете модуль игровых наград. Первая версия вашего приложения может обрабатывать только награду ЗОЛОТО, поэтому основная часть вашего кода находится внутри класса GoldReward.

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

Отличные новости, правда? А как насчет кода? В настоящее время большая часть вашего кода связана с классом GoldReward. Добавление ГЕМОВ в приложение потребует внесения изменений во всю кодовую базу. Более того, если позже вы решите добавить в приложение еще один вид НАГРАД, вам, вероятно, придется снова внести все эти изменения. В результате вы получите довольно неприятный код, пронизанный условными выражениями, которые переключают поведение приложения в зависимости от типа НАГРАД.

Решение

Шаблон фабричного метода предлагает заменить прямые вызовы построения объекта (с использованием оператора new) на вызовы специального фабричного метода. Не волнуйтесь: объекты по-прежнему создаются с помощью оператора new, но он вызывается из фабричного метода. Объекты, возвращаемые фабричным методом, часто называют продуктами.

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

Однако есть небольшое ограничение: подклассы могут возвращать разные типы продуктов, только если эти продукты имеют общий базовый класс или интерфейс. Кроме того, тип возвращаемого значения для фабричного метода в базовом классе должен быть объявлен как этот интерфейс.

Например, классы GoldReward и GemReward должны реализовывать интерфейс наград, в котором объявляется метод rewardFor. Каждый класс реализует этот метод по-разному: золотая награда увеличивает золото, награда с гемами увеличивает гемы в профиле игрока. Фабричный метод в классе GoldRewardService возвращает объекты золотой награды, тогда как фабричный метод в классе GemRewardService возвращает гемы.

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

Structure

1. Интерфейс GameItem, который является общим для всех объектов наград, которые могут быть созданы создателем и его подклассами.

  • Конкретные награды - это разные реализации интерфейса GameItem.

  • Класс ReawardCreator объявляет фабричный метод, который возвращает новые объекты наград. Важно, чтобы тип возвращаемого значения этого метода соответствовал интерфейсу продукта. Вы можете объявить фабричный метод абстрактным, чтобы заставить все подклассы реализовывать свои собственные версии метода. В качестве альтернативы базовый фабричный метод может возвращать некоторый тип награды по умолчанию. Обратите внимание: несмотря на название, создание продукта не является основной обязанностью создателя. Обычно класс создателя уже имеет некоторую базовую бизнес-логику, связанную с наградами. Фабричный метод помогает отделить эту логику от конкретных классов наград.

  • Конкретные создатели переопределяют базовый фабричный метод, поэтому он возвращает другой тип продукта. Обратите внимание, что фабричный метод не должен постоянно создавать новые экземпляры. Он также может возвращать существующие объекты из кеша, пула объектов или другого источника.

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

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

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

Как реализовать

Implementation
Implementation
  1. Создали базовый интерфейс GameItem:

  1. Создали базовый интерфейс GameItem

public interface GameItem {
   void open();
}

2. Создадим пару наград и реализуем метод интерфейса:

public class GoldReward implements GameItem {
    @Override
    public void open() {
        // todo add open business logic
        System.out.println("GoldReward opened");
    }
}

public class GemReward implements GameItem {
    @Override
    public void open() {
        // todo add open business logic
        System.out.println("GemReward opened");
    }
}

3. Дальше нам потребуется ItemGenerator, который будет открывать награды и создавать их:

public abstract class ItemGenerator {

    public void openReward() {
        // ... other code ...
        GameItem gameItem = createItem();
        gameItem.open();
    }

    /**
     * Subclasses will override this method in order to create 
     * specific reward objects.
     */
    public abstract GameItem createItem();

}

4. Давайте создадим конкретные реализации ItemGenerator:

 public class GoldGenerator extends ItemGenerator{
    @Override
    public GameItem createItem() {
        return new GoldReward();
    }
}
public class GemGenerator extends ItemGenerator{
    @Override
    public GameItem createItem() {
        return new GemReward();
    }
}

5. Протестируем, то что у нас получилось. Я буду в цикле доставать “случайный” генератор и открывать награду. В терминале можно будет увидеть сообщение об открытии награды.

public class Game {

    public static void main(String[] args) {
        Random random = ThreadLocalRandom.current();
        List<ItemGenerator> generatorList = new ArrayList<>();
        generatorList.add(new GemGenerator());
        generatorList.add(new GoldGenerator());
        
        for (int i = 0; i < 10; i ++){
            int idx = Math.abs(random.nextInt() % 2) == 0 ? 0 : 1;
            ItemGenerator itemGenerator = generatorList.get(idx);
            itemGenerator.openReward();
        }
    }
}

Ссылка на код будет вот тут. Можно посмотреть реализацию этого паттерна.

На этом разбор фабричного метода закончен. Хотелось бы узнать, встречали ли вы у себя в проектах фабричный метод? Или может вы не осознанно писали код, который получался как фабричный метод? Спасибо что дочитали до конца ​

Источник: https://habr.com/ru/post/571502/


Интересные статьи

Интересные статьи

Подозреваю, что я не один такой, кто держит дома в режиме 24/7 маленький и тихий системный блок с Windows в качестве сервера, на который можно зайти по RDP (с того же сма...
О том, что такое сервисы облачного гейминга, вряд ли стоит рассказывать — на Хабре о них писали много раз, включая меня. С каждым месяцем эти сервисы становятся все популярнее — части...
Многие современные языки поддерживают сопоставление с образцом (pattern matching) на уровне языка. Язык Java не является исключениям. И в Java 16 будет добавлено поддержка сопоставление...
Добро пожаловать на очередной сеанс ностальгической терапии! Сегодня мы снова воспользуемся машиной времени и заглянем в чью-нибудь серверную. В прошлый раз мы познакомились с масштабир...
В середине июле этого года мы рассказывали о том, что была представлена бета-версия Proxmox Backup Server (PBS). В день холостяков, 11.11.2020 в 11:11, Proxmox Server Solutions GmbH опу...