接口或类中的PHP类构造函数

18

我在思考如何构建我的类和对象时遇到了一些问题。在下面的代码中,我使用接口来定义我的类方法,但我还想将数据库连接传递给我的构造函数,以便类可以使用该连接进行工作。我按照下面的代码编写构造函数放置在我的类中,接口中的方法是否正确?

interface IDataItem
{
    public function saveItem(Item $theItem);
}

class DataItem implements IDataItem
{
    public function __construct(Database $database) 
    { 
        $this->database = $database;
    }

    public function saveItem(Item $item) 
    {       
        //save the item
    }
}

$db = new Database(); //from a database class
$dataItem = new DataItem($db);          
$dataItem->saveItem($anItem);

3
通过让数据库对象实现一个接口,你可以创建一个与特定数据库无关的应用程序,它只使用接口方法来存储数据,而不知道实际创建了哪个数据对象实例。 - GolezTrol
3
@Gordon 唉...请保持你的论点客观。我相信你有一些有效的观点,但是你带有个人偏见和挑衅性的陈述方式不必要地使它们变得晦涩难懂。 - Matthemattics
1
@Lübnah,如果您点击链接,它们是客观的。 - Gordon
@Gordon,我了解你的意思,并且并不完全不同意你的观点。但请考虑一下简单情况。有人需要一个全局访问点,只想实例化一次,并且希望延迟实例化。Singleton是最轻量级的解决方案。是的,这是一个狭窄的用例,但有时这就是所需。 - Matthemattics
显示剩余10条评论
7个回答

27

尽管在接口中添加构造函数在技术上是可行的,但接口不应定义构造函数,因为那将是实现类的实现细节。接口只应定义其他合作者可以调用的公共 API,也就是说,它们不应强制执行特定实现。

如果您在接口中放置一个要求数据库连接的构造函数,则会将具体类限制为构造函数签名中的依赖项。 如果实现接口的具体类需要不同的依赖项(因为它正在保存到 Web 服务)或附加依赖项(例如记录器),则无法使用接口来解决该问题。


1
谢谢,我忘记在接口中显式声明Item了。原帖已经被编辑过了。 - randomizer
1
这应该是截至2019年7月26日的被接受的答案。 - Timo Huovinen

13

我个人不认为你应该将构造函数放在接口中,因为如果要调用构造函数创建新对象,你必须清楚正在使用哪个实现。

你的代码有误,在接口中的方法不能有实现,它只能是一个声明,像这样:

interface IDataItem
{
    public function saveItem($theItem);
}

是的,非常抱歉,那确实是一个打字错误,在原始帖子中已经修正。 - randomizer

9

这有点偏离主题,但我认为它强调了一种情况,即在接口中定义构造函数可能是有用的。

可以在不知道类的情况下实例化一个对象。以下是一个简单的示例:

interface Animal
{
    public function __construct($name);
    public function speak();
    public function getName();
}

class Cat implements Animal
{
    protected $name=null;
    public function __construct($name){ $this->name=$name; }
    public function speak(){ echo 'meow'; }
    public function getName(){ return $this->name;}
}

class Dog implements Animal
{
    protected $name=null;
    public function __construct($name){ $this->name=$name; }
    public function speak(){ echo 'woof'; }
    public function getName(){ return $this->name;}
}

$animals=Array(array('name'=>'Felix','type'=>'Cat'),array('name'=>'Fido','type'=>'Dog'));

foreach($animals as $a)
{
    $theAnimal=new $a['type']($a['name']);
    echo '<p>speak, '.$theAnimal->getName().'</p><p>';
    $theAnimal->speak();
    echo '</p>';
}

我曾经用过类似的方法进行URL路由,将URL匹配到其顶级控制器。

6
根据PHP文档:
“请注意,可以在接口中声明构造函数,在某些情况下可能很有用,例如供工厂使用。”
重点是构造函数不必与具体类一起定义,而可以与接口一起定义,例如:
interface TestInterface {
    public function __construct(interfaceA, interfaceB);
}

然后,实现TestInterface的类使用interfaceA和interfaceB进行具体实现:

class Test implements TestInterface {
    public function __construct(testA, testB);
}

测试A可能是这样的:

class testA implements interfaceA {
    public function doStuff() {
        //...
    }
}

2

接口只是一种约定,强制要求可能无关的类都实现相同的方法。

由于在构造类时必须明确知道类,因此将构造函数放在接口中是没有意义的。您永远不会通过接口调用构造函数。您当前的解决方案是正确的。


0

如果你认为在接口中需要构造函数,或许考虑使用抽象类。


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

-4
在你的类中创建一个变量来保存数据库句柄。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接