Guy*_*kes 7 php architecture oop inheritance liskov-substitution-principle
我有一些处理程序("控制器")类,他们可以以某种方式处理项目:
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(); }
}
Run Code Online (Sandbox Code Playgroud)
但是我需要在子Item类中添加一些新功能:
class NewItem extends Item
{
public function getAuthor() { return 'author ' . rand(); }
}
Run Code Online (Sandbox Code Playgroud)
并在SecondHandler中使用它
class SecondHandler implements IHandler
{
public function execute(Item $item) { printf('%d %s, author %s', $item->getId(), $item->getTitle(), $item->getAuthor()); }
}
Run Code Online (Sandbox Code Playgroud)
但是Item
班级实际上并没有getAuthor
方法.并且,如果我尝试在SecondHandler
类中更改accept方法的签名,我将捕获E_STRICT
有关声明兼容性的错误.当然,这是一种LSP违规.
我该如何解决这个问题?例如,我是否需要两个接口,INewHandler
并且IHandler
具有不同的execute
方法签名?但它是某种代码重复.
此外,我不能使用__constructor(Item $item)
和__construct(NewItem $item)
处理程序(以及execute
没有参数的方法),这将被视为更好的解决方案:它们必须是不可变的,并且在应用程序生命周期中只允许每个策略的单个实例.
正如您自己发现的那样,PHP 的类型提示实现有很多限制,这些限制使得场景(如您所描述的场景)变得比应有的困难。在 Java 和 Swift 等其他类型语言中,您的实现绝对是合法的。
\n\n经过对你的问题的一些思考后,我找到了F\xc3\xa9lix提出的解决方案,但我认为与问题相比,它的设计过度了。
\n\n我对你的问题的回答不是解决方案,而是我在使用 PHP 开发多年后给你的建议:
\n\n放弃 PHP 中的类型提示并以动态方式进行开发。
\n\n与 Java/C++ 相比,PHP 更类似于 Ruby/Python/JavaScript,并且尝试从静态类型语言进行一对一复制会导致强制且复杂的实现。
\n\n实现问题的解决方案很简单,因此不要使其过于复杂,并保持应有的简单性(KISS 原则)。
\n\n声明不带类型的方法参数,并在真正需要的地方实施检查(例如引发异常)。
\n\ninterface IStrategy\n{\n public function execute($item);\n}\n\nclass FirstStrategy implements IStrategy\n{\n public function execute($item) {\n echo $item->getTitle();\n }\n}\n\nclass SecondStrategy implements IStrategy\n{\n public function execute($item) {\n // execute(NewItem $item) is identical to this check.\n if (! $item instanceof NewItem) {\n throw new Exception(\'$item must be an instance of NewItem\');\n }\n echo $item->getAuthor();\n }\n}\n\nclass Item\n{\n public function getId() { return rand(); }\n public function getTitle() { return \'title at \' . time(); }\n}\n\nclass NewItem extends Item\n{\n public function getAuthor() { return \'author \' . rand(); }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n如果可能,尝试不要严格强制参数的类型,而是根据可用接口(鸭子类型)调整代码的行为。
\n\nclass SecondStrategy implements IStrategy\n{\n public function execute($item) {\n $message = $item->getTitle();\n\n // PHP 5 interface availability check.\n if (is_callable([$item, \'getAuthor\'])) {\n $message .= \' \' . $item->getAuthor();\n }\n\n // With PHP 7 is even better.\n // try {\n // $message .= \' \' . $item->getAuthor();\n // } catch (Error $e) {}\n\n echo $message;\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n我希望对你有帮助。^_^
\n 归档时间: |
|
查看次数: |
503 次 |
最近记录: |