Hướng dẫn php inner class - lớp bên trong php

Intro:

Các lớp lồng nhau liên quan đến các lớp khác hơi khác 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 trong lớp kèm theo, ngay cả khi chúng được tuyên bố 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 phải đượ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 cho việc sử dụng chúng:

  • Đó là một cách nhóm các lớp nhóm hợp lý chỉ được sử dụng ở một nơi.

Nếu một lớp hữu ích cho chỉ một lớp khác, thì thật hợp lý khi 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 đóng gói.

Hãy xem xét hai lớp cấp cao nhất, A và B, trong đó B cần truy cập vào các thành viên của A mà nếu không sẽ được tuyên bố riêng tư. Bằng cách ẩn lớp B trong lớp A, các thành viên của A có thể được tuyên bố riêng tư và B có thể truy cập chúng. Ngoài ra, B tự nó có thể được ẩn 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à có thể duy 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ó hành vi tương tự 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ư pack.outerclass.innerclass, không gian tên PHP có thể sufice. Bạn thậm chí có thể khai báo nhiều hơn một không gian tên trong cùng một tệp (mặc dù, do các tính năng tải tự động tiêu chuẩn, điều đó có thể không được khuyến khích).

namespace;
class OuterClass {}

namespace OuterClass;
class InnerClass {}

Nếu bạn mong 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, thì phải mất thêm một chút nỗ lực.

Xác định 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()");
            }
        }
    }
}

Sử dụng trường hợp

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 "
Siblings
"; $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
"; } 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
"; } protected function callSibling($sibling) { echo "Call from " . get_class($this) . " -> "; $sibling->protectedMethod(); } } }

Kiểm tra

$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)

Output:

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

NOTE:

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 tốt. Tôi nghĩ rằng mã ít sạch hơn và dễ đọc. Ngoài ra, có lẽ có những cách khác để đạt được kết quả tương tự bằng cách sử dụng một mô hình được thiết lập tốt như người quan sát, mẫu trang trí ou. Đôi khi, ngay cả thừa kế đơn giản là đủ.