如何解决这个问题的最佳方法?也许为此创建了任何模式?
我有一个班级Master
,它通过DI管理一个Slave
对象。类Slave
的实例必须在内部具有父Master
。像这样:
class Master
{
private $slave;
public function __construct(Slave $slave)
{
$slave->registerMaster($this);
$this->slave = $slave;
}
}
class Slave
{
private $master;
public function registerMaster(Master $master)
{
$this->master = $master;
}
}
只有课程Master
可以调用registerMaster
方法。在这个例子中,有什么方法可以帮助我吗?
答案 0 :(得分:2)
只有类Master可以调用registerMaster方法。在这个例子中,有什么方法可以帮助我吗?
PHP中没有Friend Classes的概念,因此无法从另一个类隐藏公共方法。
您可以使用继承和受保护的可见性
abstract class LinkedEntity {
protected $master;
protected $slave;
}
class Master extends LinkedEntity {
public function __construct(Slave $slave) {
$this->slave = $slave;
$this->slave->master = $this;
}
}
class Slave extends LinkedEntity {}
$slave = new Slave;
$master = new Master($slave);
由于Master和Slave现在扩展了相同的基类,因此可以访问受保护的属性,但不能访问任何其他类。对LinkedEntity类中定义的受保护方法也是如此。因此,您可以使用protected registerMaster(Master $master)
方法并通过该方法而不是直接分配属性。
就我个人而言,我发现它并不那么漂亮,我宁愿知道你是否真的需要这里的双向关联,或者它是否足够好以实现单向关联,例如:从大师到奴隶,反之亦然。
另一个更糟糕的选择是检查callstack:
class Slave {
private $master;
public function registerMaster(Master $master)
{
if (debug_backtrace(null, 2)[1]["class"] === Master::class) {
throw new RuntimeException("Only Masters may call Slaves");
}
$this->master = $master;
}
}
该方法检查调用堆栈中的前一个类(调用registerMaster
的那个类实际上是否为Master。)但是,当您的方法依赖于被调用者的信息时,它&# 39; s通常是糟糕设计的标志。
第三种选择是通过反射来突破Slave中方法的可见性,例如
class Master {
private $slave;
public function __construct(Slave $slave) {
$this->slave = $slave;
$fn = new ReflectionMethod($slave, 'registerMaster');
$fn->setAccessible(true);
$fn->invoke($slave, $this);
}
}
class Slave {
private $master;
private function registerMaster(Master $master)
{
$this->master = $master;
}
}
同样,这不是最优的,因为OOP的核心原则是信息隐藏,我们在这里打破它。我们正在突破指定的私人能见度并强制它为公众公开。此外,这不会阻止任何其他对象做同样的事情。
答案 1 :(得分:0)
避免类中额外方法的另一种方法是将闭包绑定到另一个类的私有范围,这样您就可以直接访问所有方法和属性:
class Master
{
private $slave;
public function __construct(Slave $slave)
{
$this->slave = $slave;
$master = $this;
$masterInjector = function (Master $master) {
$this->master = $master;
};
$masterInjector->call($slave, $master);
}
}
class Slave
{
private $master;
}