更改子类中父方法的可见性范围

Posted

技术标签:

【中文标题】更改子类中父方法的可见性范围【英文标题】:Changing the visibility scope of parent methods in child classes 【发布时间】:2014-08-17 01:08:21 【问题描述】:

我有一个 Validator 类和一个从它扩展而来的 UserValidator 类。

我的Validator 有一个公共方法setRule(...) 具有公开可见性。

当我从它扩展时,我想将 setRule(...) 父方法的可见性更改为私有/受保护 子级,以便它仅对子级可见,并且没有外人可以调用此方法来自孩子。

这可能吗?如果是这样,我该如何实现?

【问题讨论】:

使用protected 可见性而不是public 但这意味着,当我的Validator 类被使用时,setRule() 方法不能被访问,因为该方法将只对类本身可见并且它只是子类。跨度> 抱歉,我以为你要的就是这个 不,你不能随意改变方法的可见性......虽然反射可以模拟这一点,如果你需要使用它是一个肮脏的黑客 【参考方案1】:

从架构的角度来看,不建议这样做。如 cmets 中所述,干净的方法是将您的方法设置为 protected,以便只有孩子可以访问它。

我想不出一个用例会让我需要在父类上调用公共方法,但不允许我在子类上调用它。

这违反了开放/封闭原则。类应该对扩展开放,而不是对修改开放。

既然这不是问题,我将提供一种方法来实现这一点。但请注意:

此方法使用了一个负责实例化的额外类 这是一个黑客。此解决方案在引发可访问性错误时不会使用 php 的本地语言功能。

首先让我们定义你已经拥有的类

<?php

class Validator 

    public function setRule()
    
        echo "Hello World";
    



class UserValidator extends Validator 

    public $prop = 'PROPERTY';


这里没有什么特别的。所以让我们继续为可见性错误创建一个自定义异常类。

<?php

class MethodNotAccessibleException extends Exception 

当我们尝试在子类上调用“伪私有”方法时,将引发此异常。

现在我们要创建负责实例化您的子类的类。它基本上只是一个包装器,它定义了一个 lock 属性,该属性包含不应访问的方法名称。

<?php

class PrivateInstanceCreator 

    protected $reflectionClass;
    protected $lock = [];
    protected $instance;

    public function __construct($classname, $args = []) 
    
        // We'll store an instance of the reflection class
        // and an instance of the real class
        $this->reflectionClass = new ReflectionClass($classname);
        $this->instance = $this->reflectionClass->newInstanceArgs($args);
        return $this;
    

    // The lock method is able to make a method on the
    // target class "pseudo-private"
    public function lock($method)
    
        $this->lock[] = $method;
        return $this;
    

    // Some real magic is going on here
    // Remember. This class is a wrapper for the real class
    // if a method is invoked we look for the method
    // in the real instance and invoke it...
    public function __call($method, $args)
    
        // ... but as soon as this method is defined as
        // locked, we'll raise an exception that the method
        // is private
        if(in_array($method, $this->lock))
        
            $reflectionMethod = $this->reflectionClass->getMethod($method);
            if($reflectionMethod->isPublic())
                throw new MethodNotAccessibleException('Method: __' . $method . '__ is private and could not be invoked');
        

        return call_user_func_array([$this->instance, $method], $args);
    

    // The same goes for properties
    // But in this case we'll do no protection
    public function __get($prop)
    
        return $this->instance->$prop;
    


我们的最后一步是实例化。

<?php

$userValidator = new PrivateInstanceCreator('UserValidator', []);
$userValidator->lock('setRule');

$userValidator->setRule(); //Will throw an exception

我们将通过使用我们的自定义包装类来实现,而不是直接实例化该类。 当然,您可以在子类本身中处理它,但这是一种无需直接接触类即可完成任务的方法。

话虽如此,它仍然是一个肮脏的黑客,如果可能的话应该避免使用它。如果您直接实例化子类,继承的方法仍然是公共的。

因此,如果开发人员对包装类一无所知,他将很难弄清楚如何正确实例化子类。

更新:

要使子类无法直接实例化,您可以将构造函数设置为private 并从反射类中调用newInstanceWithoutConstructor(),这更加肮脏,因为这将使类的Dependency Injection 完全不可能。我只是为了完整起见而提到它。 仍然不推荐使用

【讨论】:

以上是关于更改子类中父方法的可见性范围的主要内容,如果未能解决你的问题,请参考以下文章

除了 onCreate 方法之外,无法更改视图的可见性

如何从活动中更改片段中视图的可见性

为啥 Java 中没有子类可见性修饰符?

更改标签的可见性

块处理程序中变量的可见性(范围)

对比Java学Kotlin可见性修饰符