В приложении, которое я создаю, есть класс точки входа 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();
}
}
На самом деле да, это шаблон называется Абстрактная фабричная картина. Итак, пример, который я использовал, чтобы представить его перед моим классом во время моего обучения:
Так что, если вы создаете шутер от первого лица, вы можете создать три конкретные фабрики, такие как:
Все эти фабрики будут реализовывать абстрактный MonsterFactory.
Благодаря этому ваша видеоигра может создать уровень, на котором вам нужны волны монстров того же типа, поэтому вы можете использовать метод randomWaveMonsterGenerator, возвращающий MonsterFactory, который мог бы вернуть конкретный объект SwimMonsterFactory. Итак, у вас будет волна SwimMonster (ов), генерируемая SwimMonsterFactory.
Так что ответьте на ваше описание более прямо, глядя на ваш код выше, вы задали вопрос о выборе для Внедрение зависимости. С Внедрение зависимости, Я полагаю, что для этого типа шаблона вам придется внедрить каждый конкретный класс, прежде чем ваш код даже попытается получить класс реализации.
Так, например:
Надеюсь это поможет!
Других решений пока нет …