Обратите внимание, что некоторые другие ответы, возможно, могут описывать фабрики, но не описывают GOF Factory Pattern .
Теперь я хочу заменить эту строку на
Заводская модель, хотя я не уверен
так как мой конструктор TestMode требует
дополнительный объект, и я не уверен, где я
потребуется передать это значение.
Ну, вы могли бы подумать об этом так: MainMode, а не TestMode, это то, что делает особую вещь. Особая вещь, которую он делает, состоит в том, чтобы игнорировать данное число, чтобы гарантировать, что оно действительно случайное. В этом смысле MainMode делает что-то еще.
Или, если не случайность, MainMode и TestMode не отличаются, то вы, возможно, подумаете, что вы можете выделить это сходство в один класс, который предоставляет одну из двух стратегий для вычисления случайных чисел. Одна стратегия на самом деле была бы случайной, а другая - ошибочной, со случайным диапазоном только 1 значения.
Но давайте предположим, что есть и другие различия между MainMode и TestMode - предположительно TestMode выводит дополнительную отладку в System.out или что-то в этом роде.
Мы все еще можем выяснить, «как мы предоставляем случайность» из того, тестируем мы или играем в игру по-настоящему ». Это ортогональных проблем.
Итак, теперь мы знаем, что помимо того, что делает «Режим», он должен принимать стратегию случайности. Тогда мы могли бы, например, когда вам сказали, что стандартная случайная платформа на самом деле недостаточно случайна, вы можете заменить ее лучшей случайной величиной.
Или вы можете выполнить тестирование, когда диапазон случайностей ограничен только двумя вариантами, или всегда чередуется от одного до нуля, или возвращает при каждом вызове следующее значение в некотором Vecrtor или Iterator.
Итак, мы используем шаблон стратегии GOF для построения стратегий случайности:
interface RandomStrategy {
public double random();
}
public class NotSoRandom implements RandomStrategy {
private double r;
public NotSoRandom( final double r ) { this.r = r; }
public double random() { return r; }
}
public class PlatformRandom implements RandomStrategy {
public double random() { return Math.random(); }
}
Теперь, если все ваше приложение когда-либо создает только один 'Режим, вам не нужна фабрика; вы используете фабрику, когда вам нужно снова и снова создавать один и тот же тип класса; Фабрика на самом деле является просто Стратегией для создания правильного вида (под) класса.
В производственном коде я использовал фабрики, где у меня есть некоторый универсальный класс, который создает вещи, и мне нужно рассказать, как создать правильный подкласс для создания; Я прохожу на заводе, чтобы сделать это.
Теперь мы создаем шаблон Factory для режима Mode; это будет удивительно похоже на шаблон Стратегии:
abstract class Mode() {
private RandomStrategy r;
public Mode( final RandomStrategy r ) { this.r = r; }
// ... all the methods a Mode has
}
public class MainMode implements Mode {
public MainMode( final RandomStrategy r ) { super(r); }
}
public class TestMode implements Mode {
public TestMode( final RandomStrategy r ) { super(r); }
}
interface ModeFactory{
public Mode createMode( final RandomStrategy r );
}
public class MainFactory() {
public Mode createMode( final RandomStrategy r ) {
return new MainMode(r);
}
}
public class TestFactory() {
public Mode createMode( final RandomStrategy r ) {
return new TestMode(r);
}
}
Итак, теперь вы знаете о Фабричном шаблоне и Стратегическом шаблоне и о том, как они похожи по «форме», но отличаются по способу их использования: Фабричный шаблон - это Object Creational и возвращает используемый объект; Стратегия - это Поведение объекта, и экземпляр обычно создается явно, а ссылка на экземпляр хранится для инкапсуляции алгоритма. Но с точки зрения структуры они очень похожи.
Редактировать: ОП спрашивает в комментарии: «Как бы я интегрировал это в мой графический интерфейс?»
Ну, ничего из этого не относится к GUI вашей программы, кроме, возможно, 'Mode. Вы должны создать ConcreteStrategy и передать его предпочтительной фабрике в некоторой процедуре установки, возможно, определяя, какой из них использовать, основываясь на аргументах командной строки или файлах конфигурации. по сути, вы бы выбрали правильный завод очень сильно, так как вы выбираете правильный класс в исходном посте. Опять же, если вы когда-либо только создаете что-то, вам не нужна Фабрика; фабрики предназначены для массового производства (или создания семейств связанных типов бетона - хотя это выходит за рамки этого вопроса).
(Предположим, у нас есть игра, в которой пользователь может выбрать в командной строке, сражаться ли с роботами или драконами; тогда мы хотим создать экземпляр OpponentFactory, который создает Opponent (интерфейс), с производными классами RobotOpponent и DragonOpponent, и передать эту фабрику той части игры, которая вызывает spawnsNewOpponent (). Аналогично, пользователь может выбрать смелых или трусливых противников, которых мы бы определили как Стратегию. Нам не нужно создавать больше экземпляров Стратегии, так как Стратегия обычно идемпотент (без состояний и синглтон).
static int main( String[] args ) {
// setup game world
final RandomStrategy r = "random".equals(args[0])
? new PlatformRandom() : new NotSoRandom( Integer.intValue(args[0]) ) ;
// notice the simlarity to the code you originally posted;
// we factored out how to achieve "randomness" as a Strategy.
// now we will use our Strategy to setup our Factory;
final ModeFactory f = "test".equals(args[1])
? new TestFactory(r) : new MainFactory(r);
// also similar to your code
// we've just added an extra level of indirection:
// instead of creating a Mode, we've created an object that can create Modes
// of the right derived type, on demand.
// call something that uses our factory
functionThatRunsameAndNeedstoProduceModesWhenevertNeedsTo( f );
}