更改子类中父方法的可见性范围 [英] Changing the visibility scope of parent methods in child classes

查看:70
本文介绍了更改子类中父方法的可见性范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Validator类和一个从其扩展的UserValidator类.

I've got a Validator class and a UserValidator class which extends from it.

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

My Validator has a public method setRule(...) with public visibility.

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

When I extend from it I want to change the visibility of the setRule(...) parent method to private/protected within the child so that it's only visible for the child and no outsiders can call this method from the child.

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

Is that possible? If so, how could I achieve it?

推荐答案

从体系结构的角度来看,不建议这样做.正如评论中已经提到的,干净的方法是将您的方法设置为protected,这样只有孩子才能访问它.

From an architectural point of view this is not recommended. As already stated in the comments the clean way would be to set your method to protected so only children can access it.

我想不出一个用例,它使我有必要在父类上调用公共方法,但不允许在子类上调用它.

I cannot think of a single use case that would put me in the need to call a public method on a parent class but where I am not allowed to call it on the child class.

这违反了开放/封闭原则.应该打开类以进行扩展,但不能进行修改.

That's against the Open/Closed principle. Classes should be open for extension, but not for modification.

由于这不是问题,我将提供一种方法来实现.但请注意:

Since that was not the question I'll provide a way how that can be achieved though. But note:

  • 此方法利用了一个额外的类,它将负责实例化
  • 这是骇客.抛出可访问性错误时,此解决方案将不使用PHP的本地语言功能.

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

First let's define the classes you already had

<?php

class Validator {

    public function setRule()
    {
        echo "Hello World";
    }

}

class UserValidator extends Validator {

    public $prop = 'PROPERTY';

}

这里没有什么特别的.因此,让我们继续创建针对可见性错误的自定义异常类.

There's nothing special here. So let's go on and create a custom exception class for the visibility error.

<?php

class MethodNotAccessibleException extends Exception {}

当我们尝试在子类上调用伪私有"方法时,将抛出此异常.

This exception will be thrown when we try to invoke a "pseudo-private" method on the child class.

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

Now we want to create the Class that will be responsible for instantiating your child class. It is basically just a wrapper that defines a lock property which holds method names that should not be accessible.

<?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};
    }

}

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

Our final step is the instantiation.

<?php

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

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

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

Instead of instantiating the class directly we'll do it by using our custom wrapper class. Of course you could handle it in the child class itself, but that's a way to accomplish your task without touching the classes directly.

话虽如此,这仍然是一个肮脏的技巧,应尽可能避免使用.如果直接实例化子类,则继承的方法仍然是公共的.

Having said that, it is still a dirty hack whose usage should be avoided if possible. If you would instantiate the child class directly the inherited methods would still be public.

因此,如果开发人员对包装器类一无所知,他将很难找出如何正确实例化子类的方法.

So if a developer has no knowlege about the wrapper class he'll have a hard time to figure out how to instantiate the child class properly.

更新:

要直接使子类无法实例化,可以将构造函数设置为private并从反射类调用newInstanceWithoutConstructor(),这甚至更肮脏,因为这会使

To make the child class uninstantiable directly you can set the constructor to private and call newInstanceWithoutConstructor() from the reflection class, which is even dirtier, since that would make Dependency Injection for the class completely impossible. I'm just mentioning it for completenesses sake. Usage is still not recommended

这篇关于更改子类中父方法的可见性范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆