【问题标题】:Simulating friend classes in PHP在 PHP 中模拟朋友类
【发布时间】:2012-07-23 13:56:11
【问题描述】:

我有一个类Foo 有许多公共和私有方法。其中一种方法变得相当大,我想将它分叉到一个专门用于该目的的单独类中。像这样的:

<?php

class Foo
{
  //  ...

  public function doX( $a, $b )
  {
    $x = new FooXDoer;
    $x->foo = $this;
    return $x->run( $a, $b );
  }

  //  ...
}

class FooXDoer
{
  public $foo;

  public function run( $a, $b )
  {
    // ...
  }

  // ...
}

FooXDoer 可以通过$this-&gt;foo 访问Foo 的公共方法和属性。

如何让FooXDoer 访问Foo 的私有方法和属性,而不将它们公开给已经使用Foo 的其余代码?

我是否应该创建一个单独的类FooPrivate,将私有方法设为公共,将Foo 包装起来,然后让FooXDoer 引用它? FooPrivate 应该叫什么?

或者我的方法完全错误?你一般是怎么解决这个问题的?

【问题讨论】:

  • 目前还不清楚(至少对我而言)您要在这里做什么。你能用现实生活中的例子吗?
  • 如果你想在类之间分离功能,请确保类有它们的信息。如果函数 X 依赖于对象 y 的数据并对其进行操作,我建议您将功能保留在对象 y 中。
  • 我会说让他们公开,但放入 cmets 以警告其他编码人员。虽然您可能可以构建一些以编程方式阻止其他类访问的东西,但它不会很漂亮。
  • 如果你想访问私有属性,你可以将它们传递给 FooXDoe() 的构造函数。但是,您不能对私有方法执行此操作。

标签: php private friend private-members


【解决方案1】:

看起来traits 可以最好地解决您的问题,以防您使用 PHP >= 5.4。

如果没有,我想到了以下解决方案:

class A {
    private static $allowedClasses = array('B');
    private $a = 1;

    public function __get($property) {
        $caller = debug_backtrace(false);
        if(!isset($caller[1]))
            throw new Exception('Bla bla');
        if(!in_array($caller[1]['class'], self::$allowedClasses))
            throw new Exception('Bla bla');
        return $this->$property;
    }

    public function testB() {
        $b = new B();
        $b->instA = $this;
        echo $b->getA();
    }

}

class B {
    public $instA;
    public function getA() {
        return $this->instA->a;
    }
}

class C {
    public function getA() {
        $instA = new A();
        return $instA->a;
    }
}

$a = new A();
$a->testB(); // Works ok;
$c = new C();
$c->getA(); // Throws exception here;

这段代码绝对不是最佳实践 :) 但既然有可能我把它放在这里。

【讨论】:

  • 一个正确但可怕的问题解决方案。您能否简要描述一下特质如何解决这个问题?
  • Traits 可以访问使用它们的类的方法和属性。因此,您可以在 trait 中实现大型函数并使用 Foo 类的所有属性。
  • 肯定不是最佳实践,也不是生产代码,但不知何故我喜欢这个……非常有创意地使用语言特性
  • 它就像类属性的访问控制。这很疯狂,但我喜欢它!
【解决方案2】:

PHP 没有朋友类的概念,根据我的阅读,我不会说这是 php 设计者的错误决定...

恕我直言,没有通用策略,因为问题或问题过于广泛:需要考虑的因素太多:

  • run()中需要多少Foo的私有属性和方法?
  • 从抽象的角度来看:run()Foo 的关系有多密切?独立班真的“值得”吗?
  • 你会在Foo之外使用FooXDoer吗?

解决方案的两个想法:

将所需数据从foo 移交给fooDoer,可以是值对值,也可以通过在Foo 上实现compileRunData() 来返回数组或对象

public function doX( $a, $b )
{
 $x = new FooXDoer;
 $workEnvironment = $this->compileRunData();
 $x->setEnvironment( $workEnvironment );
 $x->foo = $this;
 return $x->run( $a, $b );
}

或者使用继承,尤其是受保护属性的概念:

abstract class FooAbstract
{
 protected $_basicVar1;
 protected function basicMethod1(/*...*/) {
  //...
 }
 // ...
}

abstract class FooRunner extends FooAbstract
{
 protected $_runVar1;

 protected function runMethod1(/*...*/) {
  //...
 }

 public function run($a, $b) {
  // ...
 }
}

public class Domain_Model_Foo extends FooRunner 
{

}

编辑:嘿,所以没有告诉我已经有答案了。是的,也考虑过特质,但直到现在才使用它们,所以无法真正评论它们

【讨论】:

  • 我认为继承是解决这个问题的最佳方案。
猜你喜欢
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-21
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
相关资源
最近更新 更多