บทนำ:
คลาสที่ซ้อนกันมีความสัมพันธ์กับคลาสอื่นแตกต่างจากคลาสภายนอกเล็กน้อย การใช้ Java เป็นตัวอย่าง:
คลาสที่ซ้อนกันแบบไม่คงที่สามารถเข้าถึงสมาชิกคนอื่น ๆ ของคลาสที่ปิดล้อมแม้ว่าจะประกาศว่าเป็นไพรเวตก็ตาม นอกจากนี้คลาสที่ซ้อนกันแบบไม่คงที่ยังต้องการอินสแตนซ์ของคลาสพาเรนต์เพื่อสร้างอินสแตนซ์
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
มีเหตุผลที่น่าสนใจหลายประการในการใช้:
- เป็นวิธีการจัดกลุ่มคลาสเชิงตรรกะที่ใช้ในที่เดียวเท่านั้น
ถ้าคลาสมีประโยชน์กับคลาสอื่นเพียงคลาสเดียวก็มีเหตุผลที่จะเชื่อมโยงและฝังคลาสนั้นในคลาสนั้นและทำให้ทั้งสองเข้าด้วยกัน
พิจารณาคลาสระดับบนสุด 2 คลาสคือ 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);
/* we check if a method exists, if not we throw an exception
* similar to the default error
*/
if (method_exists($this, $method)) {
/* The method exists so now we want to know if the
* caller is a child of our Package class. If not we throw an exception
* Note: This is a kind of a dirty way of finding out who's
* calling the method by using debug_backtrace and reflection
*/
$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() {
//instantiate public child inside parent
$this->publicChild = new \Package\MyParent\PublicChild();
//instantiate protected child inside parent
$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
บันทึก:
ฉันไม่คิดว่าการพยายามเลียนแบบ innerClasses ใน PHP เป็นความคิดที่ดี ฉันคิดว่าโค้ดไม่ค่อยสะอาดและอ่านง่าย นอกจากนี้อาจมีวิธีอื่นในการบรรลุผลลัพธ์ที่คล้ายคลึงกันโดยใช้รูปแบบที่กำหนดไว้อย่างดีเช่น Observer, Decorator ou COmposition Pattern บางครั้งแม้แต่การถ่ายทอดทางพันธุกรรมก็เพียงพอแล้ว