减少手动对象实例化

Reducing manual object instantiation

我正在努力学习依赖倒置原则。目前我的代码是这样的

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 实现这一点而不以我的初始代码块结束?这是一件好事吗?

使用抽象工厂class 来处理i/o 所需对象的实例化。您可以将工厂注入示例 class,或者让工厂实例化所需的对象,然后将它们注入示例 class。然后你可以这样做:

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

IOFactory 负责根据特定类型实例化输入和输出对象,然后实例化处理程序并将它们注入输入和输出对象。之后 returns 将处理程序对象注入到示例对象中。

在阅读您的问题时,我觉得您有两个主要目标。首先是提高代码的可读性(“..简化程序员的生活”),其次是将 "Example" class 与 I/O 处理程序分离。在我看来,DI 只是为了达到你的目标而遵循的原则。

在附加任何代码之前,我想强调的是,有时实际耦合代码会更好。代码必须以某种方式耦合。不要仅仅因为有人说过就到处使用 DI。正如 KISS 和 YAGNI 原则所描述的那样,简单永远是赢家。

所以这里的大问题是您的第二个目标(与 DI 解耦)是否是明智之举。 "Exmaple" class 中的 InputHandler / OutputHandler 是否有更改的真正原因?如果 "No" 是您的答案,我建议您将其原封不动地保存在 class 中。 "maybe in the distant future it will be profitable" 并不算数。

但是,如果您的处理程序对于每种类型(文件、控制台等)应该是唯一的,并且您的解耦将帮助您和其他程序员扩展平台,您可以利用工厂模式。您有多种方法来实现此模式(静态/抽象/简单/方法工厂)。主要目标是减少客户端的学习曲线,并使 "Example" class 解耦,以便添加更多类型或处理程序不会影响此 class.

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);

在您的情况下,您可以从众多可用的创意设计模式中选择一种。我的建议是使用工厂模式或对象池模式。 在工厂方法模式的情况下,你可以有一个 class 负责创建对象:

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 中给出了示例。您可以更改语法并相应地使用。这不是实现工厂模式的唯一方法。

如果你想消除在运行时创建对象的负担,你可以使用对象池模式。原型模式的混合在你的 csse 中也变得很方便。