127

新しい Web サイト用にユーザー クラスを構築していますが、今回は少し違った方法で構築することを考えていました...

C++Java、さらにはRuby (およびおそらく他のプログラミング言語) では、メイン クラス内でネストされた/内部クラスを使用できるようになっています。これにより、コードをよりオブジェクト指向で整理することができます。

PHPでは、次のようなことをしたいと思います:

<?php
  public class User {
    public $userid;
    public $username;
    private $password;

    public class UserProfile {
      // some code here
    }

    private class UserHistory {
      // some code here
    }
  }
?>

それはPHPで可能ですか?どうすれば達成できますか?


アップデート

それが不可能な場合、将来の PHP バージョンはネストされたクラスをサポートする可能性がありますか?

4

11 に答える 11

145

はじめに:

ネストされたクラスは、外部クラスとは少し異なる方法で他のクラスに関連付けられます。例として Java を取り上げます。

入れ子になった非静的クラスは、プライベートと宣言されていても、囲んでいるクラスの他のメンバーにアクセスできます。また、静的ではないネストされたクラスでは、親クラスのインスタンスをインスタンス化する必要があります。

OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);

それらを使用するいくつかの説得力のある理由があります。

  • これは、1 つの場所でのみ使用されるクラスを論理的にグループ化する方法です。

クラスが他の 1 つのクラスだけに役立つ場合、それをそのクラスに関連付けて埋め込み、2 つを一緒に保持するのが論理的です。

  • カプセル化を高めます。

2 つの最上位クラス A と B を考えてみましょう。B は、そうでなければ非公開と宣言される A のメンバーにアクセスする必要があります。クラス B をクラス A 内に隠すことで、A のメンバーを非公開と宣言し、B がそれらにアクセスできるようにします。さらに、B自体を外界から隠すことができます。

  • ネストされたクラスは、コードをより読みやすく保守しやすくすることができます。

ネストされたクラスは通常、その親クラスに関連し、一緒になって「パッケージ」を形成します

PHPで

ネストされたクラスがなくても、PHP で同様の動作を行うことができます。

Package.OuterClass.InnerClass のように、達成したいのが構造/組織だけである場合は、PHP 名前空間で十分かもしれません。同じファイルで複数の名前空間を宣言することもできます (ただし、標準の自動読み込み機能により、お勧めできない場合があります)。

namespace;
class OuterClass {}

namespace OuterClass;
class InnerClass {}

メンバーの可視性など、他の特性をエミュレートしたい場合は、もう少し手間がかかります。

「パッケージ」クラスの定義

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

ノート:

PHP で innerClasses をエミュレートしようとするのは、あまり良い考えではないと思います。コードがきれいで読みにくいと思います。また、Observer、Decorator、COMposition パターンなどの確立されたパターンを使用して、同様の結果を達成する方法が他にもあると思われます。単純な継承で十分な場合もあります。

于 2013-05-07T17:17:11.347 に答える
2

名前空間を使用して、この問題に対する洗練された解決策を書いたと思います。私の場合、内部クラスは親クラスを知る必要はありません (Java の静的内部クラスのように)。例として、'User' と呼ばれるクラスと 'Type' と呼ばれるサブクラスを作成し、この例ではユーザー タイプ (ADMIN、OTHERS) の参照として使用しました。よろしく。

User.php (ユーザークラスファイル)

<?php
namespace
{   
    class User
    {
        private $type;

        public function getType(){ return $this->type;}
        public function setType($type){ $this->type = $type;}
    }
}

namespace User
{
    class Type
    {
        const ADMIN = 0;
        const OTHERS = 1;
    }
}
?>

Using.php (「サブクラス」の呼び出し方の例)

<?php
    require_once("User.php");

    //calling a subclass reference:
    echo "Value of user type Admin: ".User\Type::ADMIN;
?>
于 2016-08-22T16:20:24.457 に答える