Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 六边形体系结构/干净代码:实现适配器模式的问题_Php_Oop_Symfony_Design Patterns_Hexagonal Architecture - Fatal编程技术网

Php 六边形体系结构/干净代码:实现适配器模式的问题

Php 六边形体系结构/干净代码:实现适配器模式的问题,php,oop,symfony,design-patterns,hexagonal-architecture,Php,Oop,Symfony,Design Patterns,Hexagonal Architecture,我目前正在Symfony 2框架上编写一个小型控制台应用程序。我试图将应用程序与框架隔离(主要是在听了一些关于六边形体系结构/端口和适配器、干净的代码以及将应用程序与框架分离的有趣演讲后作为练习),以便它可以作为控制台应用程序、web应用程序运行,或者不费吹灰之力就转移到另一个框架 我遇到的问题是,我的一个接口是使用适配器模式实现的,它依赖于另一个也使用适配器模式实现的接口。这很难描述,最好用一个代码示例来描述。在这里,我用“my”作为类/接口名称的前缀,只是为了弄清楚哪些代码是我自己的(我可以

我目前正在Symfony 2框架上编写一个小型控制台应用程序。我试图将应用程序与框架隔离(主要是在听了一些关于六边形体系结构/端口和适配器、干净的代码以及将应用程序与框架分离的有趣演讲后作为练习),以便它可以作为控制台应用程序、web应用程序运行,或者不费吹灰之力就转移到另一个框架

我遇到的问题是,我的一个接口是使用适配器模式实现的,它依赖于另一个也使用适配器模式实现的接口。这很难描述,最好用一个代码示例来描述。在这里,我用“my”作为类/接口名称的前缀,只是为了弄清楚哪些代码是我自己的(我可以编辑),哪些代码属于Symfony框架

// My code.

interface MyOutputInterface
{
    public function writeln($message);
}

class MySymfonyOutputAdaptor implements MyOutputInterface
{
    private $output;

    public function __construct(\Symfony\Component\Console\Output\ConsoleOutput $output)
    {
        $this->output = $output;
    }

    public function writeln($message)
    {
        $this->output->writeln($message)
    }
}

interface MyDialogInterface
{
    public function askConfirmation(MyOutputInterface $output, $message);
}

class MySymfonyDialogAdaptor implements MyDialogInterface
{
    private $dialog;

    public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
    {
        $this->dialog = $dialog;
    }

    public function askConfirmation(MyOutputInterface $output, $message)
    {
        $this->dialog->askConfirmation($output, $message); // Fails: Expects $output to be instance of \Symfony\Component\Console\Output\OutputInterface
    }
}

// Symfony code.

namespace Symfony\Component\Console\Helper;

class DialogHelper
{
    public function askConfirmation(\Symfony\Component\Console\Output\OutputInterface $output, $question, $default = true)
    {
        // ...
    }
}
另外需要注意的是,
\Symfony\Component\Console\Output\ConsoleOutput
实现了
\Symfony\Component\Console\Output\OutputInterface

要符合
MyDialogInterface
MySymfonyDialogAdapter::ASKConfimation
方法必须将
MyOutputInterface
的实例作为参数。但是,调用Symfony的
DialogHelper::askConfirmation
方法需要一个
\Symfony\Component\Console\Output\OutputInterface
实例,这意味着代码不会运行

我可以从以下两个方面来看待这一问题,但这两个方面都不是特别令人满意的:

  • mysymfonyoutputadapter
    实现
    MyOutputInterface
    Symfony\Component\Console\Output\OutputInterface
    。这并不理想,因为我需要指定该接口中的所有方法,而我的应用程序只关心
    writeln
    方法

  • mysymfonydialogadapter
    假设传递给它的对象是
    mysymfonyoutputadapter
    的实例:如果不是,则抛出异常。然后向
    mysymfonyoutputadapter
    类添加一个方法,以获得底层的
    \Symfony\Component\Console\Output\ConsoleOutput
    对象,该对象可以直接传递给Symfony的
    对话框助手::askConfirmation
    方法(因为它实现了Symfony的
    输出接口
    )。这将类似于以下内容:

    class MySymfonyOutputAdaptor implements MyOutputInterface
    {
        private $output;
    
        public function __construct(\Symfony\Component\Console\Output\ConsoleOutput $output)
        {
            $this->output = $output;
        }
    
        public function writeln($message)
        {
            $this->output->writeln($message)
        }
    
        public function getSymfonyConsoleOutput()
        {
            return $this->output;
        }
    }
    
    class MySymfonyDialogAdaptor implements MyDialogInterface
    {
        private $dialog;
    
        public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
        {
            $this->dialog = $dialog;
        }
    
        public function askConfirmation(MyOutputInterface $output, $message)
        {
            if (!$output instanceof MySymfonyOutputAdaptor) {
                throw new InvalidArgumentException();
            }
    
            $symfonyConsoleOutput = $output->getSymfonyConsoleOutput();
    
            $this->dialog->askConfirmation($symfonyConsoleOutput, $message);
        }
    }
    
    这感觉是错误的:如果
    MySymfonyDialogAdaptor::askConfirmation
    要求其第一个参数是mysymfonyoutputadapter的实例,则它应该将其指定为其typehint,但这意味着它不再实现
    MyDialogInterface
    。此外,在适配器外部访问底层的
    ConsoleOutput
    对象似乎并不理想,因为它实际上应该由适配器包装

  • 有人能提出一个解决这个问题的办法吗?我觉得我遗漏了什么:也许我把适配器放错地方了,而不是多个适配器,我只需要一个适配器包装整个输出/对话框系统?或者,为了实现这两个接口,我需要包含另一个继承层

    谢谢你的建议


    编辑:此问题与以下请求中描述的问题非常相似:

    经过与同事的多次讨论(感谢Ian和Owen),再加上Matthias via的一些帮助,我们提出了以下解决方案:

    <?php
    
    // My code.
    
    interface MyOutputInterface
    {
        public function writeln($message);
    }
    
    class SymfonyOutputToMyOutputAdaptor implements MyOutputInterface
    {
        private $output;
    
        public function __construct(\Symfony\Component\Console\Output\OutputInterface $output)
        {
            $this->output = $output;
        }
    
        public function writeln($message)
        {
            $this->output->writeln($message)
        }
    }
    
    class MyOutputToSymfonyOutputAdapter implements Symfony\Component\Console\Output\OutputInterface
    {
        private $myOutput;
    
        public function __construct(MyOutputInterface $myOutput)
        {
            $this->myOutput = $myOutput;
        }
    
        public function writeln($message)
        {
            $this->myOutput->writeln($message);
        }
    
        // Implement all methods defined in Symfony's OutputInterface.
    }
    
    interface MyDialogInterface
    {
        public function askConfirmation(MyOutputInterface $output, $message);
    }
    
    class MySymfonyDialogAdaptor implements MyDialogInterface
    {
        private $dialog;
    
        public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
        {
            $this->dialog = $dialog;
        }
    
        public function askConfirmation(MyOutputInterface $output, $message)
        {
            $symfonyOutput = new MyOutputToSymfonyOutputAdapter($output);
    
            $this->dialog->askConfirmation($symfonyOutput, $message);
        }
    }
    
    // Symfony code.
    
    namespace Symfony\Component\Console\Helper;
    
    class DialogHelper
    {
        public function askConfirmation(\Symfony\Component\Console\Output\OutputInterface $output, $question, $default = true)
        {
            // ...
        }
    }
    

    您可以尝试使用实现自己接口的实现,也可以扩展使用它们的symfony组件。这样,您就可以与Symfony一起实现,而无需任何额外工作。