Giriş:
İç içe geçmiş sınıflar, diğer sınıflarla dış sınıflardan biraz farklı şekilde ilişkilidir. Java'yı örnek olarak ele alalım:
Statik olmayan iç içe geçmiş sınıfların, özel olarak bildirilmiş olsalar bile çevreleyen sınıfın diğer üyelerine erişimi vardır. Ayrıca, statik olmayan iç içe geçmiş sınıflar, ana sınıfın bir örneğinin başlatılmasını gerektirir.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
Bunları kullanmanın birkaç zorlayıcı nedeni vardır:
- Yalnızca tek bir yerde kullanılan sınıfları mantıksal olarak gruplamanın bir yoludur.
Bir sınıf yalnızca bir başka sınıf için yararlıysa, onu o sınıfta ilişkilendirmek ve gömmek ve ikisini bir arada tutmak mantıklıdır.
B'nin, aksi takdirde özel ilan edilecek olan A'nın üyelerine erişmesi gereken iki üst düzey sınıfı, A ve B'yi düşünün. B sınıfını A sınıfı içinde saklayarak, A'nın üyeleri özel ilan edilebilir ve B bunlara erişebilir. Ek olarak, B'nin kendisi dış dünyadan gizlenebilir.
- İç içe geçmiş sınıflar, daha okunabilir ve bakımı yapılabilir koda yol açabilir.
İç içe geçmiş bir sınıf, genellikle üst sınıfıyla ilgilidir ve birlikte bir "paket" oluşturur
PHP'de
PHP'de yuvalanmış sınıflar olmadan benzer davranışlarınız olabilir .
Elde etmek istediğiniz tek şey, Package.OuterClass.InnerClass olarak yapı / organizasyon ise, PHP isim alanları yeterli olabilir. Hatta aynı dosyada birden fazla ad alanı bildirebilirsiniz (ancak standart otomatik yükleme özellikleri nedeniyle bu tavsiye edilmeyebilir).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
Üye görünürlüğü gibi diğer özellikleri taklit etmek istiyorsanız, biraz daha fazla çaba gerektirir.
"Paket" sınıfını tanımlama
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()");
}
}
}
}
Kullanım alanı
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();
}
}
}
Test yapmak
$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)
Çıktı:
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
NOT:
PHP'de innerClasses'ı taklit etmeye çalışmanın gerçekten iyi bir fikir olduğunu düşünmüyorum. Kodun daha az temiz ve okunaklı olduğunu düşünüyorum. Ayrıca, Gözlemci, Dekoratör veya Kompozisyon Modeli gibi iyi kurulmuş bir model kullanarak benzer sonuçlar elde etmenin muhtemelen başka yolları da vardır. Bazen basit bir miras bile yeterlidir.