1

我试图更好地理解 Liskov 原则使用的类不变性。

我知道像 D 这样的一些语言对 invariant 有本机支持,但是,在 PHP 中使用断言我尝试将魔法方法和断言结合起来:

<?php

class Person {
    protected string $name;
    protected string $nickName;
    protected function testContract(){
        assert(($this->name != $this->nickName));
    }
    public function __construct(string $name, string $nickName){
        $this->name = $name;
        $this->nickName = $nickName;
    }
    public function __set($name, $value){
        $this->testContract();
        $this->$name = $value;
    }
    public function __get($name){
        $this->testContract();
        return $this->$name;
    }
}

class GoodPerson extends Person {
    public function getFullName(){
        return $this->name." ".$this->nickName. "!!!";
    }
}

class BadPerson extends Person {
    protected function testContract(){
        assert(($this->name != ""));
    }
}

$gp = new GoodPerson("João", "Joãozinho");
echo $gp->nickName;
echo $gp->getFullName();
$bp = new BadPerson("João", "João");
echo $bp->nickName;
  • 我可以使用断言来创建合同吗?
  • BadPerson 是 Liskov 对继承的 Class Invariance 违反的有效示例吗?
  • GoodPerson 是 Liskov 的类不变性的有效例子吗?
4

1 回答 1

1

我可以使用断言来创建合同吗?

来自PHP 文档

  • 断言只能用作调试功能
  • 断言不应用于正常的运行时操作,例如输入参数检查

BadPerson 是 Liskov 在继承上违反类不变性的有效示例吗?

是的

先决条件不能在子类型中得到加强。

但是你的代码没有任何意义

首先testContract,只有在您尝试设置或获取动态属性时才会调用您的方法,并且它将检查您通过constructor

public function __set($name, $value){
    $this->testContract();
    $this->$name = $value;
}

在这里,您基本上测试了构造函数参数,但在魔术方法 ( __set)

因此,为了使该检查有效,您需要__set像这样调用

$gp = new BadPerson("João", "Joãozinho");
$gp->name = ''; // This line here invokes __set magic method

所以你真正需要做的是摆脱testContract检查并将检查放在基类构造函数中。为什么?因为您的属性是protected客户设置它们的唯一机会是通过构造函数

public function __construct(string $name, string $nickName){
    if ($name != "" && $name != $nickName)
        throw new Exception('Name must not be empty and must not equal Nickname');

    $this->name = $name;
    $this->nickName = $nickName;
}

GoodPerson 是 Liskov 的类不变性的有效例子吗?

是的

于 2020-01-13T05:28:12.293 回答