【问题标题】:PHP interface optional parameterPHP接口可选参数
【发布时间】:2017-10-18 04:32:04
【问题描述】:

我正在尝试为我的服务类创建一个通用接口。我在使用接口的两个类时遇到问题。他们共享一个名为 create 的方法。 create 方法除了三个参数。我希望它使第三个参数是可选的,以便两个类都可以使用它。

interface ServiceInterface{

    public static function create($var1, $var2, $thisOneIsOptional);

}

class ServiceObject1 implements ServiceInterface{

    public static function create($url, $server){
       //....
    }
 }

class ServiceObject2 implements ServiceInterface{

    public static function create($methode, $url, $id){
       //....
    }
}

【问题讨论】:

  • 拥有一个允许以不同方式使用该方法的界面听起来有点违反直觉。接口的全部目的是确切地知道如何使用实现类。如果一项服务不需要$id 而另一项需要,则仍应需要它。然后由服务来忽略它。
  • 公共静态函数 create($var1, $var2, $thisOneIsOptional = '');或者如果数组公共静态函数 create($var1, $var2, $thisOneIsOptional = []);
  • 你不能有一个带有可选参数的接口和一个需要它的实现——这意味着签名不兼容。如果你不能将你的接口概括到足以适应所有可能的实现,那么接口就不是你想要的。
  • 那么您不知道每个服务需要或不需要哪些参数,而无需实际查看每个服务的代码。那么,为什么还要为界面烦恼呢?
  • static 方法的接口......无论如何都很奇怪。针对接口进行编码意味着您可以将对象替换为其他对象。但是,静态方法总是在特定类上调用,不能注入/替换。那么……为什么首先要有一个界面?

标签: php oop interface


【解决方案1】:

在 PHP 56+ 中,您可以使用省略号运算符:

interface ServiceInterface {
    public static function create(...$params);
}

class ServiceObject1 implements ServiceInterface
{
    public static function create(...$params)
    {
        $url = $params[0];
        $server = $params[1];
        print "url = $url\n";
        print "server = $server\n";
    }
}

class ServiceObject2 implements ServiceInterface
{
    public static function create(...$params)
    {
        $method = $params[0];
        $url = $params[1];
        $id = $params[1];
        print "method = $method\n";
        print "url = $url\n";
        print "id = $id\n";
    }
}
print "ServiceObject1::create\n";
ServiceObject1::create("url", "server");
print "\nServiceObject2::create\n";
ServiceObject2::create("method", "url", "id");

输出:

ServiceObject1::create
url = url
server = server

ServiceObject2::create
method = method
url = url
id = url

/咆哮

对于抱怨 OP 想要做什么的用户 - 虽然一般建议是这样做是个坏主意,但在许多情况下,程序确实事先并不关心参数是什么。例如,如果函数是 sumAll 而不是 create

【讨论】:

  • 我对此表示赞同,因为它实际上回答了 OP 的问题,同时仍然提到这无论如何都不是最佳实践。我正在处理一个遗留代码库,我需要扩展一些实现接口的类。即使它违反了界面的纯粹概念,这是实现这一目标的最少破坏性和最快的方式。在我的场景中,原作者应该做了一个实现接口的抽象类,然后让所有东西都继承自抽象类。但是,... 允许我修改大约 12 个类而不是 50 个类。这是一场胜利。
【解决方案2】:

实现定义为父类或接口上的抽象签名的方法的类必须遵循某些规则,但它们不必完全匹配。

这没有很好的记录,例如它甚至没有在PHP documentation of interfaces 中提及,但在documentation of abstract classes 中有所提及:

此外,方法的签名必须匹配,即类型提示和所需参数的数量必须相同。例如,如果子类定义了一个可选参数,而抽象方法的签名没有,则签名中没有冲突。

也就是说,该方法必须能够根据签名被调用,但不排除:

  1. 在方法的实现中使用不同的变量名
  2. 在方法签名中声明的参数之后声明额外的可选参数(如果有)

从这个问题来看,这会起作用:

interface ServiceInterface{

    public static function create($var1, $var2);

}

class ServiceObject1 implements ServiceInterface{

    public static function create($url, $server){
       //....
    }
 }

class ServiceObject2 implements ServiceInterface{

    public static function create($methode, $url, $id = null){
       //....
    }
}

【讨论】:

  • 这是一项非常有见地的研究,谢谢!似乎应该修改 php 文档。您是否知道您也可以将返回类型添加到具有没有返回类型的接口的实现中?例如接口定义了public function foo();,实现可以添加string返回类型并且仍然兼容public function foo(): string {return 'bar';}
  • @KelvinJones,你是对的,我收回我的评论!
【解决方案3】:

这不是实现接口的正确方法。

首先,接口定义了一个类应该如何使用,而一个可选参数可能会打破这个原因。

除此之外,即使你只有两个参数,它们也应该有自己的含义,并且应该共享这个含义。

接口方法签名有很大区别:

public static function create($var1, $var2);

以及两个实现的方法:

public static function create($url, $server)

和:

public static function create($methode, $url)

另外,AFAIK,以这种方式实现会引发严格的标准违规,因为您正在更改接口签名。

如果你必须创建一个没有意义的共享界面,则没有理由共享甚至创建该界面。

【讨论】:

    【解决方案4】:

    你可以这样做:

    interface ServiceInterface{
    
        public static function create($url, $server, $id = null);
    
    }
    
    class ServiceObject1 implements ServiceInterface{
    
        public static function create($url, $server, $id = null){
           //....
        }
    }
    
    class ServiceObject2 implements ServiceInterface{
    
        public static function create($methode, $url, $id = null){
           //....
        }
    }
    

    但是让ServiceObject2扩展ServiceObject1不是更容易和更合乎逻辑吗,通过这样做你可以用一个额外的参数覆盖这个函数。而不是要求对象是接口的实例,而是要求它是 ServiceObject1 的实例。

    【讨论】:

    • 是的,我能做到。我计划为我当前使用的所有模型(6 个模型)创建一个服务类,并不是每个模型都有相同数量的 create 方法参数。我认为为他们所有人签订一份合同会很好。但是创建一个超类是一个很好的解决方案。只是接口在结构上更漂亮:)
    • @melkawakibi - 只是一个建议;与其考虑什么是“漂亮的”、什么是“酷孩子用的”或添加一些“只是因为”,您应该考虑编写直观代码。在我看来,这样更漂亮。
    猜你喜欢
    • 1970-01-01
    • 2011-02-03
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 1970-01-01
    • 2011-06-09
    • 2015-02-03
    • 2018-10-21
    相关资源
    最近更新 更多