php – Liskov替换原则和使用继承类的正确方法
我有一些处理程序(“控制器”)类,他们可以以某种方式处理项目:
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; } } 我希望能帮到你. ^ _ ^ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |