我试图最好地理解Liskov原理使用的类不变性。
我知道像 D 这样的语言对不变性有原生支持,但是,在 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 的继承类不变性违规的有效例子吗?
- GoodPerson是Liskov的Classe In不变性的有效例子吗?
我可以使用断言来创建合约吗?
不
来自 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的Classe In不变性的有效例子吗?
是的