PHP -使用接口,策略模式和可选方法参数


PHP - Using Interfaces, Strategy Pattern and Optional Method Parameters

我想知道我是否可以得到一些帮助。

我有一个像这样的接口

interface BackupContract {
    public function testConn($request, $port);
}

那么这个接口的2个示例实现如下

class FTPBackup implements BackupContract {
    public function testConn($request, $port = 21) {
        // code here
    }
}
class SFTPBackup implements BackupContract {
    public function testConn($request, $port = 22) {
        // code here
    }
}

因为我需要在运行时指定'service'和port,所以我使用'strategy pattern'来实现这一点,像这样。

class BackupStrategy {
    private $strategy = NULL;
    public function __construct($service) {
        switch ($service) {
            case "ftp":
                $this->strategy = new FTPBackup();
                break;
            case "sftp":
                $this->strategy = new SFTPBackup();
                break;
        }
    }
    public function testConn($request, $port)
    {
        return $this->strategy->testConn($request, $port);
    }
}

最后,在我的控制器中,我使用以下代码将它们放在一起。

$service = new BackupStrategy($request->input('service'));
$service->testConn($request, $request->input('port'));

问题是,如果用户没有输入端口,它意味着自动分配一个端口变量,即21或22,就像在两个实现。

它似乎不工作,但它没有抛出任何错误

但是你不应该在BackupStrategy的构造函数中实现这个开关。

你的做法违反了s.o.i.d原则中的"打开/关闭"原则。

每当您需要添加另一个"备份策略"时,您都需要修改 BackupStrategy类,将逻辑添加到构造函数中。

您可以创建子类,每个子类实现每个备份策略类的实现(LOL),并且只从BackupStrategy类继承必要的内容。

interface BackupContract {
    public function testConn($request, $port);
}
class FTPBackup implements BackupContract {
    public function testConn($request, $port = 21) {
        // code here
    }
}
class SFTPBackup implements BackupContract {
    public function testConn($request, $port = 22) {
        // code here
    }
}
class BackupStrategy
{
    private $strategy;
    public function testConn($request, $port)
    {
        return $this->strategy->testConn($request, $port);
    }
}
class ConcreteFTPBackup extends BackupStrategy
{
    function __construct()
    {
        $this->strategy = new FTPBackup();
    }
}
class ConcreteSFTPBackup extends BackupStrategy
{
    function __construct()
        {
        $this->strategy = new SFTPBackup();
    }
}
$service = new ConcreteFTPBackup();
$serice->testConn($request, $request->input('port'));

或者

interface BackupContract {
    public function testConn($request, $port);
}
class FTPBackup implements BackupContract {
    public function testConn($request, $port = 21) {
        // code here
    }
}
class SFTPBackup implements BackupContract {
    public function testConn($request, $port = 22) {
        // code here
    }
}
class BackupStrategy
{
    private $strategy;
    function __construct(BackupContract $bc)
    {
        $this->strategy = $bc();
    }
    public function testConn($request, $port)
    {
        return $this->strategy->testConn($request, $port);
    }
}

$service = new BackupStrategy(new FTPBackup());
$serice->testConn($request, $request->input('port'));

那么你可以在运行时实现切换。

也可以在BackupStrategy类中创建setStrategy方法,在运行时设置或更改备份策略:

public function setStrategy(BackupContract $bc)
{
    $this->strategy = $bc();
}

所以现在,您可以在运行时创建具有一个备份策略的服务,甚至可以在运行时更改策略!!Chekit:

$service = new BackupStrategy(new FTPBackup());
$service->testConn($request, $request->input('port'));

$service->setStrategy(new SFTPBackup());
$service->testConn($request, $request->input('port'));

BackupStrategy类是所有封装算法收敛的地方,但不要忘记在所有这些中"封装" !

策略模式中最重要的是可以在运行时使用的一系列算法的封装!

希望它能帮助你!

除了Simon和Laurent。

你正在使用接口,所以实现应该匹配。

一个可能的解决方案:

interface BackupContract {
    public function testConn($request, $port = 0);
}

但就我个人而言,我不喜欢这种方法。接口中的可选内容意味着您应该尽可能地验证每个实现,而不是信任实现。

其次,我建议使用类型声明(PHP7),例如:
public function foo(int $bar) : bool
{
    return true;  
}

这个方法需要一个整数作为参数(必须)和一个布尔值作为返回(必须)。使用$bar,你可以确定这个var类型是一个整数。

查看更多信息:http://php.net/manual/en/migration70.new-features.php

您总是在BackupStrategy->testConn($request, $port)方法中设置端口。如果没有指定端口,它会传递一个空字符串

我将实现一个回退函数。

@simon说,你在控制器中使用了错误的方法名

您正在调用一个不存在的testConnection();方法。实际上你要调用的是testConn();

检查你的错误报告设置,因为这肯定会抛出一个错误。