Фабричный метод

Раз тут такая пьянка пошла, то и я внесу свою лепту :)

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

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

Поясню на примере. Допустим, разрабатываем мы игру про войну. У нас есть базовый класс военной техники:
class Vehicle
{
    // ...
}

Реальная же техника наследует этот класс и реализует какую-то свою функциональность. Пусть у нас в игре будет танк, джип и пушка:
class Tank extends Vehicle
{
    // ...
}

class Jeep extends Vehicle
{
    // ...
}

class Turret extends Vehicle
{
    // ...
}

Вся эта техника умеет стрелять. Проблема в том, что стреляет она разными снарядами, которые хоть чем-то и похожи (умеют летать и попадать в цель), но ведут местами себя по-разному (например, пушечные ядра разрываются при попадании). Поэтому мы объявляем некоторый базовый класс снаряда, реализующий общую для всех функциональность:
class Weapon
{
    public function fly(): void { /* ... */ }
}

а в наследниках уже будем определять его поведение для каждого типа снаряда:
class Cannonball extends Weapon
{
    public override function fly(): void
    {
        super.fly();
        if (/* попали в цель */)
            bang(); // взорваться
    }

    // ...
}

class Bullet extends Weapon
{
    // ...
}

Теперь давайте добавим в базовый класс техники возможность стрелять:
class Vehicle
{
    public function fire(): Weapon { return null; }
}

а в классах-наследниках перегружаем метод, позволяя каждому типу техники стрелять соответствующими снарядами, на практике которые будут оказываться разными в зависимости от типа техники:
class Tank extends Vehicle
{
    public override function fire(): Weapon { return new Cannonball(); }
}

class Jeep extends Vehicle
{
    public override function fire(): Weapon { return new Bullet(); }
}

class Turret extends Vehicle
{
    public override function fire(): Weapon { return new Cannonball(); }
}

Теперь мы можем работать с однотипной техникой, которая стреляет однотипными снарядами:
var vehicles: Array = [new Tank(), new Jeep(), new Turret()];

for each (var vehicle: Vehicle in vehicles)
{
    var weapon: Weapon = vehicle.fire();
    weapon.fly();
}

в том числе можем, например, воспользоваться этим в базовом классе:
class Vehicle
{
    public function fire(): Weapon { return null; }

    public function moveAndFire()
    {
        Weapon weapon = fire();
        weapon.fly();
    }
}

// ...

Vehicle vehicle = new Tank();
vehicle.moveAndFire(); // тут внутри будет вызван метод fly() для объекта класса Cannonball, созданного танком

Комментарии (21)

+7
Каша ниочем.
Советую как можно улучшить:
А) Ответь на главные вопросы:
1. Что такое фабричный метод, и почему метод.
2. Зачем он нужен.
3. Где он у тебя в примерах вообще. А то я сначала подумал что ты под ФМ решил про принципы ООП порассказать.
Б) Очисть статью от ненужного лишнего кода (ну или расположить его хотя-бы в правильном порядке).
В) Начать с теории и перейти к практике с пояснениями.
Вот тогда получится что-то вменяемое. Хотя вот уже 4 человекам очень понравилась твоя подача материала. Хотя я полагаю, если он не знали что такое ФМ, то и не узнали.
Зы: я уже молчу про некорректные названия классов (weapon-оружие, это автомат, пистолет и т.д, а не ядро, пуля, картечь и т.д.). Хотя получилось что не молчу ;D
0
ну так минусовать нельзя ^__^
0
Движок заточен под хабр и минуса — это невозможность постить и прочие «прэлэсти» хабра. Потому их нэт.
0
1.
мы можем в базовом классе предоставить некоторый интерфейс для создания неких абстрактных объектов, а в наследниках реализовать методы, создающие уже конкретные объекты


2. Пример это демонстрирует.

3. Метод fire().
+3
И вообще пример притянут за уши, как и многие примеры. И сам этот паттерн это ж для каких олдфажных кодеров на прримитивных языках придуман, чтоб вместо переопределения нужного метода (фабричного) юзать условия в базовом классе, когда даже на Цэ без ПэПэ и даже без 0х11 (или как его щаз тамъ) есть указатели на функции, которыми можно обойтись и без всяких переопределений. А на нормальных языках есть еще переменные типа класс (var c:Class), для которых этот паттерн вообще не нужен и только портит код избыточностью. В АС3, кстати, есть и классовые переменные и функторы (переменная указатель на метод или функцию — анг: function object), поэтому использование этого паттерна на АС3 — признак плохого кода.
0
Если чесно, ничего не понял :) Ну то есть вот в твоём тексте слова все знакомые, но каким боком ты их связываешь с этим паттерном я не понимаю. Можешь пример штоле привести?
0
Не могу не позлорадствовать: это что же Шарп такой примивный язык, что в нем даже отражений нету?
0
Ну как раз таки в шарпе с рефлекшеном всё слишком даже хорошо :) Но я совершенно не понимаю, почему ты его сюда пытаешься привязать и выдаёшь за более правильное решение. Как по мне, то рефлекшн — это пушка, и не стоит из неё по воробьям палить.
0
указатель на функцию это не функтор :) ведь функтор это по определению объект «который можно использовать как функцию», но уж никак не функция.

«И сам этот паттерн это ж для каких олдфажных кодеров на прримитивных языках придуман».
Совершенно очевидно, что из-за гениального примера вы не поняли, что такое фабричный метод :) Не в обиду сказано, я сам сначала не понял, что автор имел в виду пока не вчитался в код. ФМ — отличный паттерн, но лучше почитать про него в вики. И сразу про обычные фабрики, они вообще используются в каждом втором движке.
0
И почему люди до сих пор не умеют пользоваться поисковиками, читать википедию и другие материалы:
1. http://ru.wikipedia.org/wiki/Функтор_(программирование)
В C для создания функционального объекта используются указатели на функцию
2. Я прекрастно понял и кривость изложения и сам паттерн и где он есть в статье и почему он показывает отсутствие мозгов при применении в АС3, где есть встроенный тип Class, реализующий отражения. А также функторы, которыми вопрос решается наподобие С/С++.

Я пожалуй попозже напишу как это все делается. Ели никто не сделает этого раньше.
+1
я согласен с puzzlesea — новичкам это будет не понятно, а опытным они и не нужно, да еще и придираются ^__^
поставил + за старания, ну и потому что минусовать нельзя…
+6
+1 puzzlesea. Даже в голову не пришло бы так делать имея Class. Но давайте дальше про паттерны, это интересно
+4
public override function

Магистр йода ;D
+1
а я также привык =)
0
честно говоря не помню как правильно, но FlashDevelop override ставит первым
+8
Публичную переопределить функцию — единственный вариант верный ;)
0
истину глаголит падаван юный
+1
а как надо? :) вообще это стандарт в C# как минимум.
0
override public function
Иначе как-то… Не буду придумывать — peregrimm хорошо сказал ;)
+2
Вообще да, формально это фабричный метод. Но пример настолько притянут за уши насколько это было возможно. Если вам интересно, что это это такое «фабричный метод», то не читайте пост — идите сразу в вики.

Не стоит писать про то, что не используете, но вчера услышали на лекции.

PS. Ни разу в жизни по работе за последние 6 лет не приходилось писать фабричные методы (разработка игр, не движков, не энтерпрайз-систем, просто игр). В отличие скажем от обычных фабрик. Вот пост про них был бы полезнее, судя по тому как люди (в том числе и с FGB) любят раскидать switch-case или if-else гарды по всему проекту.
0
Простите если излишне эмоционален. Но ведь кто-то возьмет ваш код за основу.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.