Сокращение ручного создания объекта

Я пытаюсь научиться принципу инверсии зависимости. В настоящее время мой код такой

class Example {
public function __construct( $input, $output ) {
$input_handler  = new InputHandler( $input );
$output_handler = new OutputHandler( $output );

$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}

$input   = new Input();
$output  = new Output();
$example = new Example( $input, $output)

Тем не менее, кажется, используя базовую инъекцию зависимостей, это должно быть больше так?

class Example {
public function __construct( $input_handler, $output_handler ) {
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}

$input          = new Input();
$output         = new Output();
$input_handler  = new InputHandler( $input );
$output_handler = new OutputHandler( $output);
$example        = new Example( $input_handler, $output_handler)

Это правильно?

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

$input          = new ConsoleInput();
$output         = new FileOutput();
$input_handler  = new ConsoleInputHandler( $input );
$output_handler = new FileOutputHandler( $output);
$example        = new Example( $input_handler, $output_handler);
$example->doStuffToOutput();

Тем не менее, я бы предпочел сделать программистов жизнь немного проще всего лишь передать тип ввода и вывода и не беспокоиться о классах, обрабатывающих их;

$input   = new ConsoleInput();
$output  = new FileOutput();
$example = new Example( $input, $output );
$example->doStuffToOutput();

или даже

$example = new Example( new ConsoleInput(), new FileOutput() );
$example->doStuffToOutput();

Как я могу добиться этого, используя DIP, а не получить мой начальный блок кода? Это хорошая вещь, чтобы сделать?

1

Решение

Пока я читал твой вопрос, я чувствовал, что у тебя две главные цели. Во-первых, улучшить читабельность вашего кода («облегчить жизнь программиста»), а во-вторых, отделить класс «Пример» от обработчиков ввода-вывода. С моей точки зрения, DI — это просто принцип, которому нужно следовать для достижения ваших целей.

Прежде чем прикреплять какой-либо код, я хочу подчеркнуть, что иногда лучше на самом деле соединить ваш код. Код должен быть как-то связан. Не используйте DI везде только потому, что это было сказано. Простота, как описано с принципами KISS и YAGNI, всегда побеждает.

Таким образом, большой вопрос здесь заключается в том, является ли ваша вторая цель (разделение с DI) разумной вещью. Есть ли реальная причина для изменения InputHandler / OutputHandler в классе «Exmaple»? Если «Нет» — ваш ответ, я бы порекомендовал вам сохранить его в этом классе нетронутым. И «возможно, в отдаленном будущем это будет выгодно», на самом деле не считается.

Однако, если ваши обработчики должны быть уникальными для каждого типа (файл, консоль и т. Д.), И ваша развязка поможет вам и другим программистам расширить платформу, вы можете воспользоваться шаблоном Factory. У вас есть несколько способов реализовать этот шаблон (статические / абстрактные / простые / методические фабрики). Основная цель состоит в том, чтобы уменьшить кривую обучения у клиента и отделить класс «Пример», чтобы добавление новых типов или обработчиков не влияло на этот класс.

class HandlerFactory {

protected static function createInputHandler(Input $input)
{
switch ($input)
{
case is_a($input, 'FileInput'):
return new FileInputHandler($input);
case is_a($input, 'ConsoleInput'):
return new ConsoleInputHandler($input);
}

throw new \Exception('Missing Input handler');
}

protected static function createOutputHandler(Output $output)
{
switch ($output)
{
case is_a($output, 'FileOutput'):
return new FileOutputHandler($output);
case is_a($output, 'ConsoleOutput'):
return new ConsoleOutputHandler($output);
}

throw new \Exception('Missing Output handler');
}

public static function createHandler($io)
{
switch ($io)
{
case is_a($io, 'Input'):
return self::createInputHandler($io);
case is_a($io, 'Output'):
return self::createOutputHandler($io);
}

throw new \Exception('Missing I/O handler');
}
}

Теперь ваш первый код в вашем вопросе все еще актуален с небольшим изменением:

class Example {
public function __construct($input, $output) {
$input_handler  = HandlerFactory::createHandler($input);
$output_handler = HandlerFactory::createHandler($output);

$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}

$input   = new Input();
$output  = new Output();
$example = new Example($input, $output);
2

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

Используйте класс Abstract Factory для обработки экземпляров объектов, необходимых для обработки ввода-вывода. Вы можете либо внедрить фабрику в примерный класс, либо позволить фабрике создать экземпляр необходимых объектов, а затем внедрить их в примерный класс. Тогда вы можете сделать:

$IOFactory = new IOFactory();
$example = new Example($IOFactory::makeInputHandler($inputType), $IOFactory::makeOutputHandler($outputType));
$example->doStuffToOutput();

IOFactory заботится о создании экземпляров базы входных и выходных объектов в их определенных типах, а затем создает экземпляры обработчиков и внедряет их в объект ввода и вывода. После этого возвращает объекты-обработчики, которые будут вставлены в пример объекта.

2

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

class ObjectFactory {
public InputHandler createInputHandlerObject(inputobj){
if( inputobj instanceOf  ConsoleInput ) {
return new ConsoleInputHandler();
} else if( inputobj instanceOf  FileInput ) {
}
}
// similarly create a method for creating OutputHandler object.
//create the appropriate object by using instanceOf operator.

Так как я знаком с Java, я привел пример на Java. Вы можете изменить синтаксис и использовать соответственно. Это не единственный способ реализовать Factory Pattern.

Если вы хотите снять нагрузку с создания объекта во время выполнения, вы можете использовать шаблон пула объектов. Гибрид прототипа шаблона также становится удобным в вашем csse.

0
По вопросам рекламы [email protected]