Intro:
Kelas bertingkat berhubungan dengan kelas lain sedikit berbeda dari kelas luar. Mengambil Java sebagai contoh:
Kelas bertingkat non-statis memiliki akses ke anggota lain dari kelas yang melingkupi, meskipun mereka dideklarasikan sebagai pribadi. Selain itu, kelas bertingkat non-statis memerlukan instance dari kelas induk untuk dibuat.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
Ada beberapa alasan kuat untuk menggunakannya:
- Ini adalah cara mengelompokkan kelas secara logis yang hanya digunakan di satu tempat.
Jika sebuah kelas hanya berguna untuk satu kelas lain, maka logis untuk menghubungkan dan menyematkannya di kelas itu dan menjaga keduanya bersama-sama.
- Ini meningkatkan enkapsulasi.
Pertimbangkan dua kelas tingkat atas, A dan B, di mana B membutuhkan akses ke anggota A yang dinyatakan sebagai pribadi. Dengan menyembunyikan kelas B di dalam kelas A, anggota A dapat dinyatakan sebagai pribadi dan B dapat mengaksesnya. Selain itu, B sendiri bisa disembunyikan dari dunia luar.
- Kelas bertingkat dapat menghasilkan kode yang lebih mudah dibaca dan dipelihara.
Kelas bersarang biasanya berhubungan dengan kelas induknya dan bersama-sama membentuk "paket"
Di PHP
Anda dapat memiliki perilaku serupa di PHP tanpa kelas bersarang.
Jika semua yang ingin Anda capai adalah struktur / organisasi, seperti Package.OuterClass.InnerClass, namespace PHP mungkin cukup. Anda bahkan dapat mendeklarasikan lebih dari satu namespace dalam file yang sama (meskipun, karena fitur autoloading standar, itu mungkin tidak disarankan).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
Jika Anda ingin meniru karakteristik lain, seperti visibilitas anggota, dibutuhkan lebih banyak usaha.
Mendefinisikan kelas "paket"
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()");
}
}
}
}
Kasus penggunaan
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();
}
}
}
Menguji
$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)
Keluaran:
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
CATATAN:
Saya benar-benar tidak berpikir mencoba meniru innerClasses di PHP adalah ide yang bagus. Menurut saya kodenya kurang bersih dan mudah dibaca. Selain itu, mungkin ada cara lain untuk mencapai hasil yang serupa dengan menggunakan pola yang sudah mapan seperti Pola Pengamat, Penghias atau Pola Komposisi. Kadang-kadang, warisan sederhana pun sudah cukup.