前端之家收集整理的这篇文章主要介绍了
php – Liskov替换原则和使用继承类的正确方法,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
@H_
301_0@
我有一些处理程序(“控制器”)类,他们可以以某种方式处理项目:
interface IHandler
{
public function execute(Item $item);
}
class FirstHandler implements IHandler
{
public function execute(Item $item) { echo $item->getTitle(); }
}
class SecondHandler implements IHandler
{
public function execute(Item $item) { echo $item->getId() . $item->getTitle(); }
}
class Item
{
public function getId() { return rand(); }
public function getTitle() { return 'title at ' . time(); }
}
但是我需要在子Item类中添加一些新功能:
class NewItem extends Item
{
public function getAuthor() { return 'author ' . rand(); }
}
并在SecondHandler中使用它
class SecondHandler implements IHandler
{
public function execute(Item $item) { printf('%d %s,author %s',$item->getId(),$item->getTitle(),$item->getAuthor()); }
}
但是Item类实际上没有getAuthor方法.并且,如果我尝试在SecondHandler类中更改accept方法的签名,我将捕获有关声明兼容性的E_STRICT错误.当然,这是一种LSP违规.
我该如何解决这个问题?我是否需要两个接口,例如,INewHandler和IHandler,具有不同的execute方法签名?但它是某种代码重复.
此外,我不能在处理程序中使用__constructor(Item $item)和__construct(NewItem $item)(以及不带参数的execute方法),这将被视为更好的解决方案:它们必须是不可变的,并且只允许每个策略的单个实例应用程序生命周期
正如您自己发现的那样,
PHP的类型
提示实现有很多限制,使得场景(如您所描述的场景)比它们应该更难.在Java和Swift等其他类型的语言中,您的实现绝对合法.
在对你的问题进行一些思考之后,我找到了Félix提出的解决方案,但是我认为与问题相比它的设计太多了.
我对你的问题的回答不是解决方案,而是我在用PHP开发多年后给你的建议:
放弃PHP中的类型提示,并以动态的方式开发它.
PHP比Java / C更类似于Ruby / Python / JavaScript,并且尝试从静态类型语言中复制1到1转换为强制和卷积实现.
解决您的实现问题很容易,所以不要过于复杂,并保持简单(KISS原则).
在没有类型的情况下声明方法的参数,并在您真正需要的地方实现检查(例如抛出异常).
interface IStrategy
{
public function execute($item);
}
class FirstStrategy implements IStrategy
{
public function execute($item) {
echo $item->getTitle();
}
}
class SecondStrategy implements IStrategy
{
public function execute($item) {
// execute(NewItem $item) is identical to this check.
if (! $item instanceof NewItem) {
throw new Exception('$item must be an instance of NewItem');
}
echo $item->getAuthor();
}
}
class Item
{
public function getId() { return rand(); }
public function getTitle() { return 'title at ' . time(); }
}
class NewItem extends Item
{
public function getAuthor() { return 'author ' . rand(); }
}
再一次,不要在Java中思考,而是尽可能地遵循鸭子打字的方式.
在可能的情况下,尽量不要严格强制参数的类型,而是根据可用的接口调整代码的行为(Duck Typing).
class SecondStrategy implements IStrategy
{
public function execute($item) {
$message = $item->getTitle();
// PHP 5 interface availability check.
if (is_callable([$item,'getAuthor'])) {
$message .= ' ' . $item->getAuthor();
}
// With PHP 7 is even better.
// try {
// $message .= ' ' . $item->getAuthor();
// } catch (Error $e) {}
echo $message;
}
}
我希望能帮到你. ^ _ ^