【问题标题】:How do you make a child constructor private when the parent is public?当父级是公共的时,如何将子构造函数设为私有?
【发布时间】:2019-05-21 01:07:17
【问题描述】:

为什么不能在子类中隐藏构造函数?

我收到以下异常:

致命错误:CIS\Logger\WPLogger::__construct() 的访问级别必须 公开(如在 Katzgrau\KLogger\Logger 类中) /builds/r2o/website/wp-content/mu-plugins/toolsets/lib/cis-logger/src/WPLogger.php 在第 12 行

超类代码(来自外部库):

    public function __construct($logDirectory, $logLevelThreshold = LogLevel::DEBUG, array $options = array()) { // ... 
    }

WPLogger.php 代码(子类):

private function __construct(string $logDirectory, string $logLevelThreshold = LogLevel::DEBUG, array $options = array()) {
    parent::__construct($logDirectory, $logLevelThreshold, $options);
         // ... some actions
    }
}

public static function getInstance(string $logFileRelative = self::DEFAULT_LOG_NAME, string $logLevelThreshold = LogLevel::DEBUG, array $options = array()) {
       // ...
}

我不想用new 实例化这个特殊的子类。由于某些原因,我希望使用getInstance() 静态创建它。我怎样才能做到这一点?

【问题讨论】:

  • 超类实现接口了吗?
  • 没有:extends AbstractLogger 但没有接口。

标签: php class oop inheritance constructor


【解决方案1】:

您可以使用继承来扩展方法的可见性。下面的例子是正确的

class A
{
   private function __construct()
   {}
}

class B extends A
{

   public function __construct()
   {}
}

但是您不能限制子类中方法的可见性。以下示例不正确

class A
{
   public function __construct()
   {}
}

class B extends A
{
   private function __construct()
   {}
}

在您的情况下,您尝试隐藏构造函数,因为您想制作单例。在许多情况下,单例是不好的做法。你应该使用 其他对象构建模式:Static FactoryService LocatorDependency Injection Container

例如,您可以通过静态工厂的简单修改来归档必要的结果:

class LoggerFactory
{
    private static $instance = null;

    public static function build()
    {
        if (is_null(static::$instance)) { 
            static::$instance = // making of the logger
        }
        return static:$instance;
    }
}

【讨论】:

  • 每个答案都提供了解决问题的好方法。就我而言,我认为你的工厂是最好的主意。
【解决方案2】:

确实没有任何方法可以强制子类接受比您要覆盖的父函数更低级别的visibility

你可能想要的是一个模拟父级但不extend它的包装类

class Singleton {
    /** @var \Katzgrau\KLogger\Logger */
    protected $logger;
    /** @var Singleton */
    protected $instance;

    private function __construct($someargs) {
         $this->logger = new \Katzgrau\KLogger\Logger($someargs);
    }

    public static function getInstance($someargs) {
         if($this->instance instanceof Singleton) return $this->instance;
         $this->instance = new self($someargs);
         return $this->instance;
    }

    /** Magic method to pass along calls to the other class */
    function __call($method, $args) {
         call_user_func_array(array($this->logger, $method), $args);
    }
}

【讨论】:

  • 对于我目前的情况也是一个好主意,我决定继续使用建议的工厂解决方案。
【解决方案3】:

您不能使子类方法(在这种情况下是构造函数,但同样的原则适用)比父类更具限制性。因为将使用该 type 的客户端代码期望具有与超类相同的合约/接口。否则,您无法透明地用您的类的实例替换超类的实例,反之亦然。

但是,您的超类依赖于从Psr\Log\LoggerInterface 实现的AbstractLogger。而且由于我确定每个人都遵循依赖倒置原则,你应该可以说WPLogger 直接从LoggerInterface 实现是可以的。所以:

use Psr\Log\LoggerInterface;

final class WPLogger implements LoggerInterface
{
    private $wrapperLogger;

    private function __construct() {}

    public static function getInstance(string $logFileRelative = self::DEFAULT_LOG_NAME, string $logLevelThreshold = LogLevel::DEBUG, array $options = array()) {
       // @TODO, implement here your singleton if you want

       $self = new self();
       $self->wrapperLogger = new \Katzgrau\KLogger\Logger(...);
       return $self;
    }

    // implement the rest of the interface, delegating to wrapperLogger
}

【讨论】:

  • 一个很好的解决方案,接口仅适用于我当前的情况,我决定使用工厂模式来解决它。
猜你喜欢
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 2017-04-30
  • 2016-12-03
  • 1970-01-01
  • 2016-01-09
  • 2010-10-13
  • 2012-03-14
相关资源
最近更新 更多