本文介绍了PHP对Java风格类泛型有答案吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在PHP中构建MVC框架后,我遇到了一个可以使用Java风格泛型轻松解决的问题。一个抽象的Controller类可能看起来像这样:

 抽象类控制器{

抽象public function addModel (型号$型号);

类Controller的子类可能只接受Model的子类。例如,ExtendedController应该只接受ReOrderableModel到addModel方法中,因为它提供了一个可以使用ExtendedController访问的reOrder()方法:

  class ExtendedController extends Controller {

public function addModel(ReOrderableModel $ model){

在PHP中,继承的方法签名必须完全相同,因此类型提示不能更改为不同的类,即使该类继承了超类中暗示的类类型。在java中,我只需要这样做:

 抽象类Controller< T> {

抽象public addModel(T模型);


类ExtendedController扩展Controller< ReOrderableModel> {

public addModel(ReOrderableModel model){

但是没有泛型支持PHP。是否有任何解决方案仍然遵守OOP原则?

编辑
我知道PHP不需要键入提示所有,但它可能是糟糕的OOP。首先,从接口(方法签名)不明显应该接受什么样的对象。因此,如果另一个开发人员想要使用该方法,那么显然需要类型X的对象,而不需要查看不良封装的实现(方法体)并打破信息隐藏原则。其次,因为没有类型安全性,该方法可以接受任何无效变量,这意味着需要手动进行类型检查和异常抛出!

解决方案

它看起来对我有效(尽管它的确会引发严格的警告),并带有以下测试用例:

  class PassMeIn 
{

}

class PassMeInSubClass extends PassMeIn
{

}

class ClassProcessor
{
public function processClass(PassMeIn $ class)
{
var_dump(get_class($ class));


$ b $ class classProcessorSubClass extends ClassProcessor
{
public function processClass(PassMeInSubClass $ class)
{
parent: :processClass($ class);
}
}

$ a =新的PassMeIn;
$ b =新的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
{
抛出新的InvalidArgumentException;
}
}
}

$ a =新的PassMeIn;
$ b =新的PassMeInSubClass;
$ c = new ClassProcessor;
$ d = new ClassProcessorSubClass;

$ c - > processClass($ a);
$ c - > processClass($ b);
$ d - > processClass($ b);
$ d - > processClass($ a);

您应该记住的一点是,这在OOP中并不是最佳实践。如果超类可以接受特定类的对象作为方法参数,则其所有子类也应该能够接受该类的对象。防止子类处理超类可以接受的类意味着您不能使用子类代替超类,并且100%确信它可以在所有情况下都能正常工作。相关实践被称为,并且其中规定,除其他外,方法参数的类型只能在子类中变得更弱,并且返回值的类型只能变得更强(输入只能变得更一般,输出只能变得更具体)。



这是一个非常令人沮丧的问题,我自己曾多次反驳过它,所以如果在特定情况下忽略它是最好的做法,那么我建议你忽略它。但是不要养成它的习惯,否则你的代码将开始发展各种微妙的相互依赖性,这将成为调试的噩梦(单元测试不会捕获它们,因为单个单元将按预期行事,它们之间的交互作用问题在哪里)。如果你忽视它,那么评论这个代码让别人知道它,这是一个故意的设计选择。

Upon building an MVC framework in PHP I ran into a problem which could be solved easily using Java style generics. An abstract Controller class might look something like this:

abstract class Controller {

abstract public function addModel(Model $model);

There may be a case where a subclass of class Controller should only accept a subclass of Model. For example ExtendedController should only accept ReOrderableModel into the addModel method because it provides a reOrder() method that ExtendedController needs to have access to:

class ExtendedController extends Controller {

public function addModel(ReOrderableModel $model) {

In PHP the inherited method signature has to be exactly the same so the type hint cannot be changed to a different class, even if the class inherits the class type hinted in the superclass. In java I would simply do this:

abstract class Controller<T> {

abstract public addModel(T model);


class ExtendedController extends Controller<ReOrderableModel> {

public addModel(ReOrderableModel model) {

But there is no generics support in PHP. Is there any solution which would still adhere to OOP principles?

EditI am aware that PHP does not require type hinting at all but it is perhaps bad OOP. Firstly it is not obvious from the interface (the method signature) what kind of objects should be accepted. So if another developer wanted to use the method it should be obvious that objects of type X are required without them having to look through the implementation (method body) which is bad encapsulation and breaks the information hiding principle. Secondly because there's no type safety the method can accept any invalid variable which means manual type checking and exception throwing is needed all over the place!

解决方案

It appears to work for me (though it does throw a Strict warning) with the following test case:

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);

If the strict warning is something you really don't want, you can work around it like this.

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);

One thing you should bear in mind though, this is strictly not best practice in OOP terms. If a superclass can accept objects of a particular class as a method argument then all its subclasses should also be able of accepting objects of that class as well. Preventing subclasses from processing classes that the superclass can accept means you can't use the subclass in place of the superclass and be 100% confident that it will work in all cases. The relevant practice is known as the Liskov Substitution Principle and it states that, amongst other things, the type of method arguments can only get weaker in subclasses and the type of return values can only get stronger (input can only get more general, output can only get more specific).

It's a very frustrating issue, and I've brushed up against it plenty of times myself, so if ignoring it in a particular case is the best thing to do then I'd suggest that you ignore it. But don't make a habit of it or your code will start to develop all kinds of subtle interdependencies that will be a nightmare to debug (unit testing won't catch them because the individual units will behave as expected, it's the interaction between them where the issue lies). If you do ignore it, then comment the code to let others know about it and that it's a deliberate design choice.

这篇关于PHP对Java风格类泛型有答案吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-27 03:49