소개 :
중첩 클래스는 외부 클래스와는 약간 다른 방식으로 다른 클래스와 관련됩니다. Java를 예로 들면 :
비 정적 중첩 클래스는 private로 선언 된 경우에도 바깥 쪽 클래스의 다른 멤버에 액세스 할 수 있습니다. 또한 비 정적 중첩 클래스에는 부모 클래스의 인스턴스를 인스턴스화해야합니다.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
이를 사용하는 몇 가지 강력한 이유가 있습니다.
- 한 곳에서만 사용되는 클래스를 논리적으로 그룹화하는 방법입니다.
클래스가 하나의 다른 클래스에만 유용하면 해당 클래스에 연결하고 포함하고 두 클래스를 함께 유지하는 것이 논리적입니다.
두 개의 최상위 클래스 A와 B를 고려하십시오. 여기서 B는 그렇지 않으면 private으로 선언 될 A의 멤버에 액세스해야합니다. 클래스 A 내에 클래스 B를 숨기면 A의 멤버를 private으로 선언하고 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 ou COmposition Pattern과 같이 잘 확립 된 패턴을 사용하여 유사한 결과를 얻을 수있는 다른 방법이있을 수 있습니다. 때로는 단순한 상속만으로도 충분합니다.