Является ли хорошей практикой создание фабричной реализации, которая использует другие фабрики для создания конечного конкретного объекта?

В приложении, которое я создаю, есть класс точки входа CLI:

class CLIEntryPoint {

protected $factory;

public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->factory = $factory;
}

public function run(...$args) {
$choice = $args[1];
$appObject = $this->factory->makeApplicationObject($choice);
$appObject->doApplicationRelatedStuff();
}
}

Эта точка входа создается с использованием Dependency Injection в моем скрипте «front controller» и получает ApplicationObjectFactoryInterface реализация (на самом деле текущая реализация ApplicationObjectFactoryInterface вводится контейнером DI, который, в свою очередь, читает его из файла конфигурации, но это не главное).

Текущая реализация ApplicationObjectFactoryInterface также использует DI и зависит от других фабрик, которые помогают ему создавать результирующий объект приложения:

class CurrentImplementationOfApplicationObjectFactory implements ApplicationObjectFactoryInterface {

protected $someComponentFactory;
protected $anotherComponentFactory;

public function __construct(SomeComponentFactoryInterface $someComponentFactory, AnotherComponentFactoryInterface $anotherComponentFactory) {
$this->someComponentFactory = $someComponentFactory;
$this->anotherComponentFactory = $anotherComponentFactory;
}

/**
* Interface's method
*
* @return ApplicationObjectInterface
*/
public function makeApplicationObject($choice) {
$component = $this->someComponentFactory->makeSomeComponent();
$anotherComponent = $this->anotherComponent->makeAnotherComponent();
switch ($choice) {
case 1:
return new CurrentImplementationOfApplicationObject1($component, $anotherComponent);
case 2:
return new CurrentImplementationOfApplicationObject2($component, $anotherComponent);
default:
return new DefaultImplementationOfApplicationObject($component, $anotherComponent);
}
}

}

Вот CurrentImplementationOfApplicationObject1, CurrentImplementationOfApplicationObject2 а также DefaultImplementationOfApplicationObject все реализуют ApplicationObjectInterface интерфейс и, следовательно, все они имеют doApplicationRelatedStuff метод.

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

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

Это считается хорошей практикой?

Спасибо за внимание, как всегда!

РЕДАКТИРОВАТЬ: Я посмотрел на статью Стивена и попытался реорганизовать CLIEntryPoint. Единственная проблема сейчас, кажется, состоит в том, как передать $choice параметр на заводе, который теперь находится внутри прокси, когда run() метод называется. Эта структура кода лучше, чем та, которую я выложил выше? Конечно, SomeComponentFactoryInterface а также AnotherComponentFactoryInterface должны следовать тому же поведению (фабрика, которая их использует, должна использовать их не напрямую, а через два прокси, которые реализуют, по порядку, SomeComponentInterface а также AnotherComponentInterface). Надеюсь, я все правильно понял, вот код:

class CLIEntryPoint {

protected $applicationObject;

public function __construct(ApplicationObjectInterface $applicationObject) {
$this->applicationObject = $applicationObject;
}

public function run(...$args) {
$choice = $args[1]; // How do I deal with different choices when I am using a Proxy? I should have different application objects depending on input.
$this->applicationObject->doApplicationRelatedStuff();
}
}

interface ApplicationObjectInterface {

public function doApplicationRelatedStuff();

}

class ApplicationObjectProxy implements ApplicationObjectInterface {

protected $applicationObjectFactory;
protected $applicationObjectImplementation = NULL;

public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->applicationObjectFactory = $factory;
}

public function __call($method, $args) {
// Calling interface's
$implementation = $this->getImplementation();
$methodOfInterfaceToCall = preg_replace('/Proxy$/', '', $method);
return $implementation->{$methodOfInterfaceToCall}(...$args);
}

/**
* Laxy loading method.
*/
protected function getImplementation() {
if (is_null($this->applicationObjectImplementation)) {
$this->applicationObjectImplementation = $this->applicationObjectFactory->makeApplicationObject(); // Choice should go here somehow...
}
return $this->applicationObjectImplementation;
}

public function doApplicationRelatedStuff() {
// This will call the PHP's magic `__call` method, which in turn will forward the call to the application object's
// implementation returned by the factory.
return $this->doApplicationRelatedStuffProxy();
}

}

0

Решение

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

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

  1. FlyingMonsterFactory
  2. SwimmingMonsterFactory
  3. WalkingMonsterFactory,

Все эти фабрики будут реализовывать абстрактный MonsterFactory.

Благодаря этому ваша видеоигра может создать уровень, на котором вам нужны волны монстров того же типа, поэтому вы можете использовать метод randomWaveMonsterGenerator, возвращающий MonsterFactory, который мог бы вернуть конкретный объект SwimMonsterFactory. Итак, у вас будет волна SwimMonster (ов), генерируемая SwimMonsterFactory.


Так что ответьте на ваше описание более прямо, глядя на ваш код выше, вы задали вопрос о выборе для Внедрение зависимости. С Внедрение зависимости, Я полагаю, что для этого типа шаблона вам придется внедрить каждый конкретный класс, прежде чем ваш код даже попытается получить класс реализации.

Так, например:

  • Ваш код выше говорит, что метод run дает аргумент под названием
    выбор.
  • С этим выбор, вам придется использовать его в качестве параметра в getImplementation метод.
  • Все конкретные объекты, которые используют метод getImplementation зависимость
    впрыскивание
    должны быть созданы до того, как вы позвоните getImplementation метод.
  • Но так как вы не знаете, какой класс реализации будет вызываться, я считаю, что вы должны внедрить ВСЕ классы реализации заранее.
  • Тогда вы можете использовать выбор переменная в качестве параметра, чтобы получить правильный реализованный класс фабрики.

Надеюсь это поможет!

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector