Объясняет фабричные методы с помощью основ защиты башни.

Метод Factory позволяет создавать объекты в соответствии с инструкциями из любой другой части кода (клиент). Клиент не всегда может решить, какой из нескольких объектов-кандидатов создавать экземпляры.

Вы можете разделить паттерны на 3 категории: творческие, структурные и поведенческие. Этот паттерн относится к категории творческих. Вы поймете, почему.

Фабричный метод позволяет клиенту делегировать создание объекта, сохраняя при этом контроль над созданным типом объекта. Вы создаете объекты, вызывая функцию-конструктор, используемую с оператором new.

Когда и зачем использовать фабричный метод

Расширяемость является ключом к фабричному методу.

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

Поэтому вы можете абстрагировать их создание от общего подхода.

Вы найдете геймифицированный пример с более простыми общими свойствами. Я выбрал пример со спаунером монстров, но об этом позже.

Обзор шаблона

Чтобы соответствовать шаблону, вам нужны 3 объекта кодирования:

  • Создатель:В примере кода: Factory. Объект factory, создающий новый monster (products). Необходимо реализовать FactoryMethod, который возвращает вновь созданный monster (product).
  • AbstractProduct. Поскольку в JavaScript нет абстрактного способа работы, в JavaScript нельзя создать абстрактный объект. На любом другом языке он объявил бы интерфейс для продуктов. Используйте TypesScript для включения интерфейсов.
  • Продукт. В приведенном ниже примере Monster и создаваемый продукт. Этот шаблон позволяет всем продуктам поддерживать один и тот же интерфейс (свойства и методы).

Пример 1: Фабрика монстров (4 типа)

В этом примере используется MonsterFactory для создания 4 типов monsters.

const MonsterFactory = function () {
   // ---snip---
};

У каждого типа монстров разные хитпойнты. Метод createMonster() объекта Factory — это Factory Method.

const MonsterFactory = function () {
    this.createMonster = function (type) {
        let monster;

        switch (type) {
            case 'common':
                monster = new common();
                break;
            case 'rare':
                monster = new rare();
                break;
            case 'elite':
                monster = new elite();
                break;
            case 'boss':
                monster = new boss();
                break;
        }
        // ---snip---

        return monster;
    }
};

4 типа монстров должны быть определены, чтобы позже вызывать их по-разному и редактировать их по отдельности (когда желательны изменения):

const common = function () {
    this.hitPoints = "25";
};

const rare = function () {
    this.hitPoints = "80";
};

const elite = function () {
    this.hitPoints = "190";
};

const boss = function () {
    this.hitPoints = "970";
};

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

jafactory.createMonster("common")

Помните, что Javascript не поддерживает абстрактные классы или интерфейсы. Следовательно, AbstractProduct (из диаграммы выше) не реализован.

Мы должны убедиться, что все монстры имеют одинаковые свойства и методы.

Всего будет создано четыре разных типа монстров, и все они будут храниться в одном массиве.

Теперь вы можете вызвать его уже и вывести их на консоль:

function run() {

    var monsters = [];
    var factory = new MonsterFactory();

    monsters.push(factory.createMonster("common"));
    monsters.push(factory.createMonster("rare"));
    monsters.push(factory.createMonster("elite"));
    monsters.push(factory.createMonster("boss"));

    console.log(monsters);
}

run();

Но было бы намного круче, если бы мы позволили им говорить самим за себя. Вы можете добиться этого, добавив адаптацию MonsterFactory:

const MonsterFactory = function () {
    this.createMonster = function (type) {
        let monster;

        switch (type) {
            case 'common':
                monster = new common();
                break;
            case 'rare':
                monster = new rare();
                break;
            case 'elite':
                monster = new elite();
                break;
            case 'boss':
                monster = new boss();
                break;
        }

        // add here
        monster.type = type;

        monster.info = function () {
            console.log(`Hi I am a ${this.type} monster.`);
            console.log(`I have ${this.hitPoints} hitpoints.`);
        }
        // until here

        return monster;
    }
};

И функция run() тоже:

function run() {

    var monsters = [];
    var factory = new MonsterFactory();

    monsters.push(factory.createMonster("common"));
    monsters.push(factory.createMonster("rare"));
    monsters.push(factory.createMonster("elite"));
    monsters.push(factory.createMonster("boss"));

    // add here
    for (var i = 0, len = monsters.length; i < len; i++) {
        monsters[i].info();
    }
    // until here
}

И это будет ваш вывод:

Весь код примера:

const MonsterFactory = function () {
    this.createMonster = function (type) {
        let monster;

        switch (type) {
            case 'common':
                monster = new common();
                break;
            case 'rare':
                monster = new rare();
                break;
            case 'elite':
                monster = new elite();
                break;
            case 'boss':
                monster = new boss();
                break;
        }

        monster.type = type;

        monster.info = function () {
            console.log(`Hi I am a ${this.type} monster.`);
            console.log(`I have ${this.hitPoints} hitpoints.`);
        }

        return monster;
    }
};

const common = function () {
    this.hitPoints = "25";
};

const rare = function () {
    this.hitPoints = "80";
};

const elite = function () {
    this.hitPoints = "190";
};

const boss = function () {
    this.hitPoints = "970";
};

function run() {

    var monsters = [];
    var factory = new MonsterFactory();

    monsters.push(factory.createMonster("common"));
    monsters.push(factory.createMonster("rare"));
    monsters.push(factory.createMonster("elite"));
    monsters.push(factory.createMonster("boss"));

    for (var i = 0, len = monsters.length; i < len; i++) {
        monsters[i].info();
    }
}

run();

Пример 2: Фабрика монстров для Tower Defense

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

Теперь вы увидите, как легко этот шаблон упрощает разработку:

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

Цель этого примера состоит в том, чтобы каждую секунду порождать монстра, всего до 10 монстров в виде одной волны:

  • 7 общих
  • 1 элита (монстр №6)
  • 1 редкий (монстр #8)
  • 1 босс (монстр №10)

Алгоритм нереста плохой и сделан в образовательных целях :)

Код почти тот же, за исключением нескольких изменений тут и там. Я отметил их как:

// begin of change
changes
changes
changes
// end of change

Фабрика монстров

Вместо monster.info() я использовал monster.spawn(), где у каждого монстра будет указан его тип и его очки жизни.

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

const MonsterFactory = function () {
    this.createMonster = function (type) {
        let monster;

        switch (type) {
            case 'common':
                monster = new common();
                break;
            case 'rare':
                monster = new rare();
                break;
            case 'elite':
                monster = new elite();
                break;
            case 'boss':
                monster = new boss();
                break;
        }

        monster.type = type;

        // begin of change
        monster.spawn = function () {
            console.log(`A ${this.type} monster with ${this.hitPoints} HP has been spawned.`);
        }
        // end of change

        return monster;
    }
};

Продукты, также известные как Monster (без изменений)

const common = function () {
    this.hitPoints = "25";
};

const rare = function () {
    this.hitPoints = "80";
};

const elite = function () {
    this.hitPoints = "190";
};

const boss = function () {
    this.hitPoints = "970";
};

Клиент

Клиенту нужны важные обновления, чтобы создать 10 монстров.

Вы используете masterInterval с интервалом продолжительностью 1000 миллисекунд. Сохранение его в переменной позволит вам clearInterval() позже. В противном случае он будет работать вечно.

function run() {
    var factory = new MonsterFactory();

    // begin of change
    let counter = 0;
    const masterInterval = setInterval(() => {

        if (counter === 9) {
            factory.createMonster("boss").spawn();
        }
        else if (counter === 7) {
            factory.createMonster("rare").spawn()
        }
        else if (counter === 5) {
            factory.createMonster("elite").spawn()
        }
        else if (counter >= 0) {
            factory.createMonster("common").spawn()
        }
        counter++;
    }, 1000)

    setTimeout(() => {
        clearInterval(masterInterval)
    }, 11000)
    // end of change
}

run();

С setTimeout() в 11000 миллисекунд вы гарантируете появление ровно 10 монстров (7 обычных, 1 элитный, 1 редкий и 1 босс). В течение тайм-аута вы очищаете интервал, поэтому нерест прекращается, и ваша волна отправляется.

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

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

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord . Заинтересованы в хакинге роста? Ознакомьтесь с разделом Схема.