我正在努力学习依赖倒置原则。目前我的代码是这样的
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实现此目的,而不是最终使用我的初始代码块?这是一件好事吗?
答案 0 :(得分:2)
使用Abstract Factory类来处理处理i / o所需的对象的实例化。您可以将工厂注入示例类,或让工厂实例化所需的对象,然后将它们注入示例类。然后你可以这样做:
$IOFactory = new IOFactory();
$example = new Example($IOFactory::makeInputHandler($inputType), $IOFactory::makeOutputHandler($outputType));
$example->doStuffToOutput();
IOFactory负责以其特定类型实例化输入和输出对象,然后实例化处理程序并使用输入和输出对象注入它们。然后返回要在示例对象中注入的处理程序对象。
答案 1 :(得分:2)
在我阅读你的问题时,我觉得你有两个主要目标。首先是为了提高代码的可读性(让程序员的生活更轻松),其次是解耦"示例"来自I / O处理程序的类。就我而言,DI只是为了实现目标而遵循的原则。
在我附加任何代码之前,我想强调一下,有时候实际结合代码会更好。代码必须以某种方式耦合。不要因为已经说过而到处使用DI。正如KISS和YAGNI原则所描述的那样,简单性始终是赢家。
因此,最重要的问题是你的第二个目标(与DI脱钩)是否是明智之举。是否存在" Exmaple"中的InputHandler / OutputHandler的真正原因?上课要改变?如果"否"是你的答案,我建议你把它保持在这个类中。并且"也许在遥远的未来它将是有利可图的"并不算数。
但是,如果您的处理程序对于每种类型(文件,控制台等)都应该是唯一的,并且您的解耦将帮助您和其他程序员扩展平台,您可以利用Factory模式。您有几种方法可以实现此模式(静态/抽象/简单/方法工厂)。主要目标是减少客户的学习曲线,并制作"示例" 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);
答案 2 :(得分:0)
在您的情况下,您可以选择其中一种可用的创作设计模式。我的建议是使用工厂模式或对象池模式。 在工厂方法模式的情况下,您可以拥有一个负责创建对象的类:
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中变得很方便。