Giới thiệu:
Các lớp lồng nhau liên quan đến các lớp khác hơi khác so với các lớp bên ngoài. Lấy Java làm ví dụ:
Các lớp lồng nhau không tĩnh có quyền truy cập vào các thành viên khác của lớp bao quanh, ngay cả khi chúng được khai báo là riêng tư. Ngoài ra, các lớp lồng nhau không tĩnh yêu cầu một thể hiện của lớp cha được khởi tạo.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
Có một số lý do thuyết phục để sử dụng chúng:
- Đó là một cách nhóm các lớp một cách hợp lý chỉ được sử dụng ở một nơi.
Nếu một lớp chỉ hữu ích cho một lớp khác, thì hợp lý là liên hệ và nhúng nó vào lớp đó và giữ hai lớp lại với nhau.
- Nó làm tăng khả năng đóng gói.
Hãy xem xét hai lớp cấp cao nhất, A và B, nơi B cần quyền truy cập vào các thành viên của A mà nếu không sẽ được khai báo là private. Bằng cách ẩn lớp B trong lớp A, các thành viên của A có thể được khai báo là riêng tư và B có thể truy cập chúng. Ngoài ra, bản thân B có thể bị che giấu khỏi thế giới bên ngoài.
- Các lớp lồng nhau có thể dẫn đến mã dễ đọc và dễ bảo trì hơn.
Một lớp lồng nhau thường liên quan đến lớp cha của nó và cùng nhau tạo thành một "gói"
Trong PHP
Bạn có thể có tương tự hành vi trong PHP mà không cần các lớp lồng nhau.
Nếu tất cả những gì bạn muốn đạt được là cấu trúc / tổ chức, như Package.OuterClass.InnerClass, thì không gian tên PHP có thể tốt. Bạn thậm chí có thể khai báo nhiều không gian tên trong cùng một tệp (mặc dù, do các tính năng tự động tải chuẩn, điều đó có thể không được khuyến khích).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
Nếu bạn muốn mô phỏng các đặc điểm khác, chẳng hạn như khả năng hiển thị của thành viên, bạn sẽ phải nỗ lực hơn một chút.
Định nghĩa lớp "gói"
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()");
}
}
}
}
Ca sử dụng
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();
}
}
}
Thử nghiệm
$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)
Đầu ra:
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
GHI CHÚ:
Tôi thực sự không nghĩ rằng cố gắng mô phỏng các lớp bên trong trong PHP là một ý tưởng hay. Tôi nghĩ rằng mã kém sạch sẽ và dễ đọc. Ngoài ra, có lẽ có nhiều cách khác để đạt được kết quả tương tự bằng cách sử dụng một mẫu đã được thiết lập tốt như Người quan sát, Người trang trí hoặc Mẫu bố cục. Đôi khi, chỉ cần thừa kế đơn giản là đủ.