Intro:
Verschachtelte Klassen beziehen sich etwas anders auf andere Klassen als äußere Klassen. Am Beispiel von Java:
Nicht statisch verschachtelte Klassen haben Zugriff auf andere Mitglieder der einschließenden Klasse, auch wenn sie als privat deklariert sind. Für nicht statisch verschachtelte Klassen muss außerdem eine Instanz der übergeordneten Klasse instanziiert werden.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
Es gibt mehrere zwingende Gründe für ihre Verwendung:
- Auf diese Weise können Klassen, die nur an einer Stelle verwendet werden, logisch gruppiert werden.
Wenn eine Klasse nur für eine andere Klasse nützlich ist, ist es logisch, sie in Beziehung zu setzen und in diese Klasse einzubetten und die beiden zusammenzuhalten.
- Es erhöht die Einkapselung.
Betrachten Sie zwei Klassen der obersten Ebene, A und B, in denen B Zugriff auf Mitglieder von A benötigt, die andernfalls als privat deklariert würden. Durch das Ausblenden von Klasse B in Klasse A können die Mitglieder von A als privat deklariert werden und B kann auf sie zugreifen. Außerdem kann B selbst vor der Außenwelt verborgen werden.
- Verschachtelte Klassen können zu besser lesbarem und wartbarem Code führen.
Eine verschachtelte Klasse bezieht sich normalerweise auf ihre übergeordnete Klasse und bildet zusammen ein "Paket".
In PHP
Sie können ein ähnliches Verhalten in PHP ohne verschachtelte Klassen haben.
Wenn Sie nur Struktur / Organisation wie Package.OuterClass.InnerClass erreichen möchten, sind PHP-Namespaces möglicherweise ausreichend. Sie können sogar mehr als einen Namespace in derselben Datei deklarieren (obwohl dies aufgrund der Standardfunktionen für das automatische Laden möglicherweise nicht ratsam ist).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
Wenn Sie andere Merkmale emulieren möchten, z. B. die Sichtbarkeit von Mitgliedern, ist der Aufwand etwas höher.
Definieren der "Paket" -Klasse
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()");
}
}
}
}
Anwendungsfall
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();
}
}
}
Testen
$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)
Ausgabe:
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
HINWEIS:
Ich denke wirklich nicht, dass der Versuch, innerClasses in PHP zu emulieren, eine so gute Idee ist. Ich denke, der Code ist weniger sauber und lesbar. Es gibt wahrscheinlich auch andere Möglichkeiten, ähnliche Ergebnisse mit einem gut etablierten Muster zu erzielen, wie z. B. dem Observer, Decorator oder COmposition Pattern. Manchmal reicht schon eine einfache Vererbung aus.