Intro:
Le classi annidate si riferiscono ad altre classi in modo leggermente diverso dalle classi esterne. Prendendo Java come esempio:
Le classi nidificate non statiche hanno accesso ad altri membri della classe che la racchiude, anche se sono dichiarate private. Inoltre, le classi nidificate non statiche richiedono l'istanza di un'istanza della classe genitore.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
Ci sono diversi validi motivi per usarli:
- È un modo per raggruppare logicamente le classi che vengono utilizzate solo in un posto.
Se una classe è utile solo per un'altra classe, è logico metterla in relazione e incorporarla in quella classe e tenerle insieme.
- Aumenta l'incapsulamento.
Considera due classi di primo livello, A e B, in cui B ha bisogno dell'accesso ai membri di A che altrimenti sarebbero dichiarati privati. Nascondendo la classe B all'interno della classe A, i membri di A possono essere dichiarati privati e B può accedervi. Inoltre, B stesso può essere nascosto al mondo esterno.
- Le classi annidate possono portare a codice più leggibile e gestibile.
Una classe annidata di solito si riferisce alla sua classe genitore e insieme formano un "pacchetto"
In PHP
Puoi avere un comportamento simile in PHP senza classi nidificate.
Se tutto ciò che vuoi ottenere è struttura / organizzazione, come Package.OuterClass.InnerClass, gli spazi dei nomi PHP potrebbero essere sufficienti. Puoi persino dichiarare più di uno spazio dei nomi nello stesso file (sebbene, a causa delle funzionalità di caricamento automatico standard, ciò potrebbe non essere consigliabile).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
Se desideri emulare altre caratteristiche, come la visibilità dei membri, ci vuole un po 'più di impegno.
Definizione della classe "pacchetto"
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()");
}
}
}
}
Caso d'uso
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();
}
}
}
analisi
$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)
Produzione:
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
NOTA:
Non credo proprio che provare ad emulare innerClasses in PHP sia una così buona idea. Penso che il codice sia meno pulito e leggibile. Inoltre, ci sono probabilmente altri modi per ottenere risultati simili utilizzando un modello ben consolidato come Observer, Decorator o COmposition Pattern. A volte è sufficiente anche una semplice eredità.