在使用 PHP 构建 MVC 框架时,我遇到了一个可以使用 Java 样式泛型轻松解决的问题。一个抽象的 Controller 类可能看起来像这样:
abstract class Controller {
abstract public function addModel(Model $model);
可能存在类Controller的子类应该只接受Model的子类的情况。例如 ExtendedController 应该只接受 ReOrderableModel 到 addModel 方法中,因为它提供了一个 ExtendedController 需要访问的 reOrder() 方法:
class ExtendedController extends Controller {
public function addModel(ReOrderableModel $model) {
在 PHP 中,继承的方法签名必须完全相同,因此类型提示不能更改为不同的类,即使该类继承了父类(super class)中提示的类类型。在java中我会简单地这样做:
abstract class Controller<T> {
abstract public addModel(T model);
class ExtendedController extends Controller<ReOrderableModel> {
public addModel(ReOrderableModel model) {
但是 PHP 中没有泛型支持。是否有任何解决方案仍然符合 OOP 原则?
编辑 我知道 PHP 根本不需要类型提示,但它可能是糟糕的 OOP。首先,从接口(interface)(方法签名)来看,应该接受什么样的对象并不明显。因此,如果另一个开发人员想要使用该方法,那么显然需要 X 类型的对象,而无需查看封装不良并违反信息隐藏原则的实现(方法体)。其次,由于没有类型安全,该方法可以接受任何无效变量,这意味着需要手动进行类型检查和异常抛出!
最佳答案
以下测试用例似乎对我有用(尽管它确实引发了严格警告):
class PassMeIn
{
}
class PassMeInSubClass extends PassMeIn
{
}
class ClassProcessor
{
public function processClass (PassMeIn $class)
{
var_dump (get_class ($class));
}
}
class ClassProcessorSubClass extends ClassProcessor
{
public function processClass (PassMeInSubClass $class)
{
parent::processClass ($class);
}
}
$a = new PassMeIn;
$b = new PassMeInSubClass;
$c = new ClassProcessor;
$d = new ClassProcessorSubClass;
$c -> processClass ($a);
$c -> processClass ($b);
$d -> processClass ($b);
如果您真的不想要严格的警告,您可以像这样解决它。
class ClassProcessor
{
public function processClass (PassMeIn $class)
{
var_dump (get_class ($class));
}
}
class ClassProcessorSubClass extends ClassProcessor
{
public function processClass (PassMeIn $class)
{
if ($class instanceof PassMeInSubClass)
{
parent::processClass ($class);
}
else
{
throw new InvalidArgumentException;
}
}
}
$a = new PassMeIn;
$b = new PassMeInSubClass;
$c = new ClassProcessor;
$d = new ClassProcessorSubClass;
$c -> processClass ($a);
$c -> processClass ($b);
$d -> processClass ($b);
$d -> processClass ($a);
不过,您应该记住一件事,这在 OOP 术语中严格来说不是最佳实践。如果父类(super class)可以接受特定类的对象作为方法参数,那么它的所有子类也应该能够接受该类的对象。阻止子类处理父类(super class)可以接受的类意味着您不能使用子类代替父类(super class),并且 100% 确信它会在所有情况下工作。相关做法称为Liskov Substitution Principle它指出,除其他外,方法参数的类型只能在子类中变得更弱,返回值的类型只能变得更强(输入只能变得更一般,输出只能变得更具体)。
这是一个非常令人沮丧的问题,我自己也多次反对它,所以如果在特定情况下忽略它是最好的做法,那么我建议你忽略它。但是不要养成习惯,否则您的代码将开始开发各种微妙的相互依赖关系,这将是调试的噩梦(单元测试不会捕获它们,因为各个单元将按预期运行,这是它们之间的交互问题出在哪里)。如果您确实忽略了它,请评论代码以让其他人知道它并且这是一个经过深思熟虑的设计选择。
关于java - PHP 对 Java 风格的类泛型有答案吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9646397/