简介:
嵌套类与外部类的关系略有不同。以Java为例:
非静态嵌套类可以访问封闭类的其他成员,即使它们被声明为私有。此外,非静态嵌套类需要实例化父类的实例。
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
使用嵌套类有几个令人信服的理由:
- 这是一种逻辑上将仅在一个位置中使用的类进行分组的方式。
如果一个类只对另一个类有用,则将其与该类相关联并嵌入该类中并将两者保持在一起是合乎逻辑的。
考虑两个顶级类A和B,其中B需要访问A的成员,否则这些成员将被声明为私有。通过将类B隐藏在类A中,可以声明A的成员为私有,并且B可以访问它们。此外,B本身也可以被隐藏在外部世界之外。
嵌套类通常与其父类相关,并一起形成一个“包”。
在PHP中
即使没有嵌套类,您也可以在PHP中实现类似的行为。
如果你想要的只是结构/组织,例如Package.OuterClass.InnerClass,PHP命名空间可能足够使用。您甚至可以在同一文件中声明多个命名空间(尽管由于标准自动加载功能,这可能不可取)。
namespace
class OuterClass {}
namespace OuterClass
class InnerClass {}
如果您想要模拟其他特性,比如成员可见性,需要多付出一些努力。
定义“package”类
namespace {
class Package {
/* protect constructor so that objects can't be instantiated from outside
* Since all classes inherit from Package class, they can instantiate eachother
* simulating protected InnerClasses
*/
protected function __construct() {}
/* This magic method is called everytime an inaccessible method is called
* (either by visibility contrains or it doesn't exist)
* Here we are simulating shared protected methods across "package" classes
* This method is inherited by all child classes of Package
*/
public function __call($method, $args) {
//class name
$class = get_class($this);
if (method_exists($this, $method)) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
if (isset($trace[2])) {
$ref = new ReflectionClass($trace[2]['class']);
if ($ref->isSubclassOf(__CLASS__)) {
return $this->$method($args);
}
}
throw new \Exception("Call to private method $class::$method()");
} else {
throw new \Exception("Call to undefined method $class::$method()");
}
}
}
}
使用案例
namespace Package {
class MyParent extends \Package {
public $publicChild;
protected $protectedChild;
public function __construct() {
$this->publicChild = new \Package\MyParent\PublicChild();
$this->protectedChild = new \Package\MyParent\ProtectedChild();
}
public function test() {
echo "Call from parent -> ";
$this->publicChild->protectedMethod();
$this->protectedChild->protectedMethod();
echo "<br>Siblings<br>";
$this->publicChild->callSibling($this->protectedChild);
}
}
}
namespace Package\MyParent
{
class PublicChild extends \Package {
//Makes the constructor public, hence callable from outside
public function __construct() {}
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
class ProtectedChild extends \Package {
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
}
测试
$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)
输出:
Call from parent -> I'm Package protected method
I'm Package protected method
Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context
注意:
我真的不认为在PHP中尝试模拟内部类是一个好主意。我认为代码不够清晰易读。此外,可能有其他方法可以使用已经成熟的模式(如观察者、装饰器或组合)来实现类似的结果。有时,甚至简单的继承就足够了。
User
,例如:public class UserProfile extends User
和public class UserHistory extends User
。 - Dave Chen