PHP 接口(interface)和参数继承

标签 php interface extend

我的项目中有实体和存储库。为了简化,我有

  • 实体接口(interface)
  • 用户实体
  • 商业实体

接口(interface):

interface Entity
{
    /**
     * @return EntityId
     */
    public function getId();
}

实现

class UserEntity implements Entity
{
    /**
     * @return EntityId
     */
    public function getId(){
      //...do something here for return
      return $userId;
    }
}

class BusinessEntity implements Entity
{
    /**
     * @return EntityId
     */
    public function getId(){
      //...do something here for return
      return $userId;
    }
}

我想定义一个 Repository 基本功能,比如 save,所以我的界面看起来像这样:

interface Repository
{
    /**
     * @param Entity $entity
     *
     * @throws \InvalidArgumentException If argument is not match for the repository.
     * @throws UnableToSaveException If repository can't save the Entity.
     *
     * @return Entity The saved entity
     */
    public function save(Entity $entity);
}

后来,我对不同类型的存储库有不同的接口(interface),比如 UserRepositoryBusinessRepository

interface BusinessRepository extends Repository
{
    /**
     * @param BusinessEntity $entity
     *
     * @throws \InvalidArgumentException If argument is not match for the repository.
     * @throws UnableToSaveException If repository can't save the Entity.
     *
     * @return Entity The saved entity
     */
    public function save(BusinessEntity $entity);
}

上面的代码失败了,因为Declaration must be compatible with Repository...

但是 BusinessEntity 实现了 Entity,因此它是兼容的。

我有很多类型的实体,所以如果我不能进行类型提示,我总是需要检查传递的实例是否是我需要的 instanceof。这很愚蠢。

以下代码再次失败:

class BusinessRepository implements Repository
{
    public function save(BusinessEntity $entity)
    {
      //this will fail, however BusinessEntity is an Entity
    }
}

最佳答案

一般来说,方法参数必须相对于继承层次结构或不变量是逆变的。这意味着当用作方法参数的类型时,BusinessEntity 确实与实体“兼容”。

从“契约(Contract)”的角度来考虑。您的接口(interface) Repository promises 它的方法 save 可以处理 Entity 类型的参数。从 Repository 继承的子类型应该绑定(bind)到这个引入的契约(否则,如果您不能确定类型 promise 能够做什么,那么首先定义类型有什么意义?) .

现在,如果一个子类型突然只接受更特殊的类型,比如 BusinessEntity,但不再接受 Entity,契约就被打破了。您不能再将 BusinessRepository 用作 Repository,因为您不能使用 Entity 调用 save

起初这是违反直觉的,但看看这个:https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_argument_type

注意图中的继承箭头。

要做什么?摆脱继承是面向对象编程中的 chalice 的想法。大多数时候,它不是,并引入了各种讨厌的耦合。例如,支持组合而不是继承。看看Parameter type covariance in specializations .

关于PHP 接口(interface)和参数继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31657261/

相关文章:

javascript - 使用户能够在新窗口中返回上一页

php - 使用 composer 离线安装 Symfony

c# - MongoDB C# 驱动程序 - 将集合序列化为接口(interface)

Javascript。什么时候必须在子类声明中使用构造函数?

php - Yii2 在运行时设置数据库连接

php - 股票价格数据 - 当前和历史 - 付费/免费

pointers - 无法反射(reflect)不返回指针,编译器在转换时出现 panic ?

c# - 访问者模式——接口(interface)与抽象类

javascript - jQuery 插件 - 初始化后更新设置

javascript - JavaScript 中数字方法的奇怪语法