【问题标题】:PHP 7 return type inheritancePHP 7 返回类型继承
【发布时间】:2017-12-20 16:31:07
【问题描述】:

我已经在我的项目中实现了存储库模式,没有太多问题。为了避免重复自己,我实现了一个abstract 存储库类,我的所有存储库都扩展了它。每个存储库都有自己必须实现的interface

我有类似于以下的代码,可以正常工作:

class Model {}

class User extends Model {}

abstract class AbstractRepository
{
    protected $model;

    public function find($id)
    {
        $class = $this->model;

        return new $class();
    }
}

interface UserRepositoryInterface
{
    public function find($id);
}

class UserRepository extends AbstractRepository implements UserRepositoryInterface
{
    protected $model = User::class;
}

我想开始在我的代码中使用返回类型声明,已升级到 PHP 7.1。所以我在AbstractRepositoryfind 方法中添加了?Model 返回类型,在UserRepositoryInterface 中添加了?User 返回类型。

class Model {}

class User extends Model {}

abstract class AbstractRepository
{
    protected $model;

    public function find($id): ?Model
    {
        $class = $this->model;

        return new $class();
    }
}

interface UserRepositoryInterface
{
    public function find($id): ?User;
}

class UserRepository extends AbstractRepository implements UserRepositoryInterface
{
    protected $model = User::class;
}

现在,PHP 抱怨声明不兼容,这是我预料之中的。

致命错误:AbstractRepository::find($id) 的声明必须与 AbstractRepositoryInterface::find($id): ?Model in /Users/jonathon/Desktop/test.php 第 21 行兼容

在另一种语言中,例如 Java,我会考虑使用泛型来实现存储库,这将允许我在方法签名中使用泛型类型。

是否有可能让这个工作:

  • 我为每个存储库都有一个接口和实现
  • 我有一个由我的每个存储库扩展的抽象类,它完成了大部分工作。
  • 我可以继续使用 PHP 7 的返回类型声明,为每个不同的存储库指定不同的返回类型(例如,?User 用于 UserRepositoryInterface?Product 用于 ProductRepositoryInterface)?

按照我现在的看法,我有几个选择:

  • 不要费心使用返回类型。
  • 将所有返回类型声明更改为?Model,我的所有模型都对其进行了扩展。
  • 重复自己,摆脱抽象类

【问题讨论】:

  • 如果我正确理解您的问题,我相信这是关于覆盖抽象函数的返回类型?这现在可以在本月初发布的 PHP 7.2 中实现。
  • @AntonyD'Andrea 我无法理解,3v4l.org/5PlP0
  • @AntonyD'Andrea 感谢您的回答。我刚刚在本地安装了 PHP 7.2 并尝试运行上面的代码。我刚刚看到了同样的错误。然而,引入的变化看起来很有希望,我的第一反应是“就是这样!”。还是谢谢。
  • 我也遇到了这个问题。似乎与参数类型提示的工作方式完全不一致。也刚刚升级到7.2.2,我仍然看到同样的错误 - 在这里描述了我的情况:3v4l.org/PnIAZ - 以及我目前的工作(使用文档块)3v4l.org/D6HAu
  • 我也遇到了同样的问题。仅仅通过遵守 OOPs 继承规则,这应该可以工作,对吧?任何从抽象继承的 repo 都将通过 find-method 返回一个类型,该类型是 abstracts repo find-method 返回类型的扩展。因此它将履行合同。每个“用户”都足以作为“模型”等等。

标签: php oop generics return-type


【解决方案1】:

我相信您组织这些存储库的方式存在轻微的设计缺陷,如果没有这些缺陷,问题就会消失。

所以我在我的 AbstractRepository 中的 find 方法中添加了一个 ?Model 返回类型,并在 UserRepositoryInterface 中添加了一个 ?User 返回类型。

为什么要改变类型?一个用户应该是一个模型,只需使用模型作为类型提示。

为了避免重复自己,我实现了一个抽象存储库类,我的所有存储库都扩展了该类。每个存储库都有自己必须实现的接口。

拥有一个一对一的接口:具体的类比率是一种代码味道,这不是接口的用途。看来您真正需要的是一个 Repository 接口:

interface Repository
{
    public function find($id): Model;
}

抽象存储库实现的:

abstract class AbstractRepository implements Repository
{
    protected $model;

    public function find($id): Model
    {
        $class = $this->model;

        return new $class();
    }
}

这样 UserRepository 可以扩展它(不需要实现它,因为抽象已经实现了它)。由于$model 属性已经定义,最好在构造函数中赋值。

class UserRepository extends AbstractRepository
{
    public function __construct()
    {
        $this->model = User::class;
    }
}

类型提示不再有问题。


意见如下。我不喜欢继承。我发现这是解决不良对象关系的最可靠和最快的方法。我会完全跳过抽象类,让每个存储库实现 Repository 接口。在实践中,存储库的查找功能一直不同,因此我们无论如何都需要在具体类中覆盖它们。

interface Repository
{
    public function find($id): Model;
}


class UserRepository implements Repository
{
    public function find($id): Model
    {
        return new User();
    }
}

最后的细枝末节:模型是一个糟糕的名字。它可能是实体。 MVC 中的 Model 是指 Model layer,我们可以在其中找到实体、服务、映射器。更多阅读here.

【讨论】:

  • 我不得不不同意拥有一对一的接口 - 类是一种代码味道。我有一个单一接口和一个具体类的原因是我可以依赖抽象,而不是依赖倒置原则 (SOLID) 中推荐的具体化。不过,我很想知道你为什么这么认为? UserModel 是绝对正确的,但是通过将 Model 定义为返回类型,可以返回任何模型(A ProductCategory)。这就是为什么我想限制只有 User 的实例应该从这些方法返回。
  • 另外,我同意 Model 是一个糟糕的名称选择,而 Entity 更好。我通常选择实体,但是模型是我使用的框架的约定,提供的代码只是为了说明一个观点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 2012-04-05
  • 2017-03-26
  • 1970-01-01
  • 2016-08-01
  • 1970-01-01
相关资源
最近更新 更多