phpclass/클래스활용2004. 7. 12. 10:00
객체지향언어에 따라 같은 의미를 나타내는 용어가 서로 다르게 표현되기 때문에 혼돈을 방지하기 위해 여기에 정리하여 둡니다.
함수, 변수 및 상수
PHP 변수 함수(또는 메소드)
스몰토크 인스턴스변수 메소드
자바 변수(인스턴스변수,클래스변수) 메소드
자바스크립트 속성(property) 메소드
C++ 데이터멤버 멤버함수
Zend 엔진 2.0부터 클래스 내에서만 사용하게 되는 상수를 정의할 수 있게 되었습니다. 따라서 데이터멤버에는 변수뿐만 아니라 상수도 포함됩니다. 이에 따라 멤버변수만을 가지고는 상수까지 포함할 수가 없게 되었네요. Zend 엔진 2.0 문서를 보면 상수와 변수를 모두 포함할 때 프로퍼티(property)라는 용어를 사용하고 있습니다.
< Zend 엔진 2.0이 지원하는 클래스 멤버 >
클래스 멤버 의미
상수 상수는 상수 값을 나타내는 클래스 멤버입니다.
변수 변수는 변수 값을 나타내는 클래스 멤버입니다.
생성자 인스턴스 생성자는 클래스의 인스턴스를 초기화하는 데 필요한 동작을 구현하는 멤버입니다.
소멸자 소멸자는 클래스의 인스턴스를 소멸시키는데 필요한 작동을 구현하는 멤버입니다.
메소드 메소드는 개체나 클래스에 의해 수행될 수 있는 계산이나 작업을 구현하는 멤버입니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:24
다중 상속(Multiple Inheritance)
다중 상속이란?
다중 상속(multiple inheritance)은 자식클래스가 하나 이상의 부모클래스로부터 그 특성을 상속받는 것을 말합니다. 다중 상속된 자식클래스는 모든 부모클래스의 멤버변수 및 멤버함수의 특성을 다 가지게 되므로 부모클래스의 멤버변수 및 멤버함수를 사용할 수 있습니다.
다중상속의 필요성
객체지향 프로그래밍을 하다보면 많은 경우에 접하게 되는 것이 다중 상속 개념입니다. 다중 상속이야말로 특성 상속의 가장 기본적인 것이라고 할 수 있습니다.
 
< 다중 상속의 예 >
그림의 예에서 보듯이 서로 독립적인 4개의 클래스(더하기, 빼기, 곱하기, 나누기 클래스)로부터 멤버변수 및 멤버함수를 상속받은 계산기 클래스가 만들어지게 됩니다.
다중 상속으로 생기는 문제점
다중 상속이 가진 문제점은 클래스 상속 구조가 매우 복잡해지고, 멤버 특성의 상속 관계가 모호해 진다는 것입니다.
예를 들면, 만일 동일한 이름의 멤버변수 x가 더하기, 빼기 클래스에서 각각 정의되어 있을 때 더하기, 빼기 클래스를 다중으로 상속받게 되는 계산기 클래스에서 멤버변수 x에 접근할 때 더하기 클래스에 있는 멤버변수 x에 접근해야 할지 아니면 빼기 클래스에 있는 멤버변수 x에 접근해야 할지 혼란이 발생하게 됩니다. 따라서 각 클래스에서 멤버변수를 정의할 때는 하위클래스에서 동일한 이름의 멤버변수를 참조할 경우를 대비하지 않으면 안됩니다. 이러한 문제는 멤버함수를 정의할 때도 동일하게 나타나는 문제입니다.
멤버변수의 경우라면 이러한 것을 방지할 목적으로 Zend 엔진 2.0부터 지원되는 private 멤버변수를 이용하는 것도 좋은 방법이라고 생각됩니다. 이와같이 멤버변수를 private로 지정할 경우에 자식클래스에서 동일한 이름의 멤버를 접근하는데 혼란이 발생하지는 않을 것입니다.
PHP에서의 다중 상속
Zend 엔진 1.0
Zend 엔진 1.0에서는 단일 상속에 관하여는 별문제없이 그 부모클래스의 특성을 상속받을 수 있습니다. 그러나 복수의 부모클래스의 특성을 상속받을 수는 없습니다. 이러한 다중상속의 문제는 웹사이트 구조가 복잡해 질 수록 절실해지게 되며, 자원(클래스)의 재사용이란 측면에서 볼 때도 꼭 필요한 기능이라고 할 수 있습니다.
Zend 엔진 2.0
다중 상속은 역시 객체지향 프로그래밍에서 뜨거운 감자인 것 같습니다. Zend Engine 2.0 설계 초안에서 포함되어 발표되었던 다중 상속 개념이 정식으로 릴리즈된 Zend 엔진 2.0 알파 버전에서는 쏙 빠져 버렸습니다. 다중 상속 개념이 Zend 엔진 2.0 정식 버전에 포함될 지는 미지수입니다.
2001년 11월경에 발표된 Zend 엔진 2.0 설계 초안에 의하면 다중 상속의 경우는 아래와 같이 표기될 것으로 보입니다.
[code php;gutter:false] class child extends parent1, parent2, … {

}; [/code]
자바에서는 오로지 단일 상속만 지원합니다. 단일 상속이 클래스 계층 구조를 단순화할 수 있기는 하지만 공통된 동작을 필요로 하는 계층 구조를 구현하기에는 매우 제한적이라는 문제가 발생하지요. 자바에서는 이러한 문제를 피하기 위해(해결하기 위해?) 인터페이스라는 계층구조를 제공합니다. 자바 인터페이스는 추상적인 메소드 정의와 상수만을 포함하고 있으며 멤버변수(인스턴스 변수) 또는 메소드를 구현하고 있지는 않습니다.
Zend 엔진 2.0이 기본적으로 자바의 객체 모델을 기반으로 설계되었다는 것이 이유가 될 지는 모르겠으나 Zend 엔진 2.0에서도 직접적으로 다중 상속을 지원하기 보다는 자바와 같이 인터페이스와 같은 별도의 계층구조를 지원하는 것은 어떨까 하는 생각이 듭니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:21
이름공간(namespace)
이름공간은 현재 동작하는 프로그램에 정의되어 있는 상수명, 변수명, 함수명, 클래스명 등과 같은 이름을 기록하기 위한 공간에 관련된 매카니즘을 총칭하는 것으로 이러한 이름을 기록하기 위한 심볼 테이블을 관리합니다.
Zend 엔진 1.0에서의 스코프
Zend 엔진 1.0에서는 아래와 같이 3가지의 스코프(유효 범위)만 제공하였습니다.
  • 전역 스코프(global scope)
  • 클래스 스코프(class scope)
  • 함수 스코프(function scope)
변수는 모든 스코프에 다 포함될 수 있으며, 함수는 클래스 및 전역 스코프에만 포함될 수 있으며, 상수 및 클래스는 단지 전역 스코프에만 포함될 수 있습니다.
< 각 스코프의 특성 (o:포함가능, x:포함불가능) >
멤 버 전역 스코프 클래스 스코프 함수 스코프
변  수

o

o

o

함  수

o

o

x

클래스

o

x

x

상  수

o

x

x

이것은 Zend Engine 1.0의 모든 스코핑 방식이 심볼 이름의 충돌 문제를 해결하는데는 본질적으로 제한적이었다는 것을 의미합니다.
Zend 엔진 2.0에서의 스코프
Zend 엔진 2.0에서는 클래스 안의 클래스(Nested class)가 가능해졌다고 앞에서 살펴본 바 있습니다. 이는 바로 nested 이름공간(nested namespace)을 지원함으로 그 의미가 분명해 지는 것이지요. nested 이름공간 개념을 도입하여 모든 종류의 심볼을 포함하는 다중 심볼 테이블을 정의할 수 있도록 함으로써 심볼의 충돌의 문제를 해결할 수 있도록 한 것입니다.
< 각 스코프의 특성 (o:포함가능, x:포함불가능) >
멤 버 전역 스코프 클래스 스코프

함수 스코프

변  수

o

o

o

함  수

o

o

x

클래스

o

o

x

상  수

o

o

x

Zend 엔진 2.0에서는 현재의 클래스 이름공간을 인식하고 있으며 심볼을 탐색할 때 현재의 이름공간의 심볼테이블을 우선적으로 찾게됩니다.
현재 이름공간의 심볼 테이블을 가장 먼저 탐색하게 됩니다. 따라서 아래의 예에서 보면 클래스 FooClass 내에서는 클래스 상수 foo가 같은 이름의 전역 상수 foo를 덮어 버리기 때문에 'bar'가 아니라 'foobar'을 출력합니다.
[code php;gutter:false] <?php
define('foo','bar');

class FooClass {
const foo = 'foobar';

function printFoo() {
print foo;
}
}
?> [/code]
[code php;gutter:false] <?php
class FooClass {
function foo() {
$this->bar();
bar();
}

function bar() {
print "foobar\n";
}
}

$obj = new FooClass;
$obj->foo();
?> [/code]
아래의 예에서 bar()의 경우를 보면 Zend 엔진 1.0에서는 전역 스코프를 갖는 전역함수 bar()를 찾았을 것입니다. 그러나 Zend 엔진 2.0에서는 먼저 클래스 스코프를 갖는 메소드 bar()를 찾습니다.
따라서 위의 소스를 실행하게 되면 bar() 메소드가 현재의 이름공간에 존재하기 때문에 "foobar"를 2번 출력합니다.
접근자(accessor)
self::
클래스 지역 심볼에 접근하기 위해서 아래의 예와 같이 클래스 접근자(class accessor) 'self::'를 이용할 수 있습니다. 여기서 self::$my_static_name는 현재 클래스에 정의되어 있는 멤버변수를 의미하며 self::MY_CONSTANT는 현재 클래스에 정의되어 있는 클래스 상수를 의미합니다.
[code php;gutter:false] self::$my_static_name = self::MY_CONSTANT; [/code]
클래스명::
self 접근자 대신에 MyClass:과 같은 클래스의 이름을 사용할 수 있습니다.
[code php;gutter:false] MyClass::$my_static_name = MyClass::MY_CONSTANT; [/code]
상수 및 함수에 접근할 때 클래스를 지정하지 않는다면 현재의 클래스에서 먼저 찾게될 것입니다. 만약 현재 클래스에서 해당 심볼을 찾지 못한다면 그 다음에는 전역 스코프에서 찾을 것입니다. 따라서 전역 함수와 같은 이름으로 메소드를 정의할 때에는 주의할 필요가 있습니다.
main::
만약 현재 위치에 관계없이 전역 스코프에서만 찾기를 원한다면 main:: 접근자를 이용하면 됩니다.
[code php;gutter:false] main::strlen();
main::MY_CONSTANT [/code]
예를 들면 main::strlen()은 main 스코프에 있는 strlen() 함수를 호출하게 됩니다.
import 키워드
상수, 함수 또는 클래스를 매우 자주 사용하는데 타자속도가 매우 느리기때문에 때로는 클래스 접근자(예를 들면 MyClass ::)를 통해 이러한 심볼에 다루는 것이 불편할 수도 있습니다. 이러한 경우에는 import 키워드를 가지고 다른 이름공간에 있는 함수, 클래스 및 상수를 현재의 이름공간으로 손쉽게 가져올 수 있습니다.
[code php;gutter:false] <?php
class MyClass {
class MyClass2 {
function hello() {
print "Hello, World in MyClass2\n";
}
}

function hello() {
print "Hello, World\n";
}
}

import function hello, class MyClass2 from MyClass;

MyClass2::hello();
hello();
?> [/code]
 클래스 MyClass의 내부클래스로 정의된 MyClass2는 클래스 MyClass 외부에서는 접근할 수 없습니다. 즉, MyClass에 해당하는 이름공간의 심볼 테이블에만 기록되어 있으므로 다른 이름공간에서는 접근할 수 없는 것이지요. 그러나 import class MyClass2 from MyClass;와 같이 import 키워드를 이용하여 MyClass 이름공간에 있는 심볼 MyClass2를 전역 이름공간으로 가져올 수 있기 때문에 MyClass2::hello();와 같이 MyClass2를 전역 스코프로 접근할 수 있습니다.
클래스 MyClass의 멤버로 정의된 hello() 메소드도 원래 '객체->' 또는 '클래스명::'을 통하지 않고는 접근할 수 없으나 import function hello from MyClass;와 같이 import 키워드를 이용하게 되면 hello 함수 심볼을 전역 이름공간으로 가져올 수 있기 때문에 전역 스코프를 갖는 메인함수처럼 hello();와 같이 메소드명만으로 호출할 수 있습니다.
[code php;gutter:false] <?php
class MyOuterClass {
class MyInnerClass {
function func1() {
print "func1()\n";
}

function func2() {
print "func2()\n";
}
}
}

import class * from MyOuterClass;
import function func2 from MyOuterClass::MyInnerClass;

MyInnerClass::func1();
func2();
?> [/code]
 클래스 MyOuterClass의 정의된 모든 심볼을 가져오려면 '*' 기호를 이용합니다. 내부클래스에 있는 심볼을 가져오기위해서는 내부클래스의 이름공간을 '외부클래스::내부클래스'로 표기하여 가져올 수 있습니다.
[code php;gutter:false] <?php
class MyOuterClass {
const Hello ="Hello, World\n";
}

import const Hello from MyOuterClass;
print Hello;
?> [/code]
위의 예는 클래스 MyOuterClass에 정의된 상수 Hello를 전역 이름공간으로 가져오는 예입니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:20
클래스 상수(Class constant)
클래스 내에서만 사용하게 되는, 즉 클래스 스코프(class scope)를 갖게 되는 상수를 정의하여 사용할 수 있습니다. 따라서 클래스 상수는 클래스 내의 모든 메소드만이 접근할 수 있으며 클래스 외부에서는 접근할 수 없습니다.
그러나 메소드 내에서만 사용할 수 있는 지역 상수(local constant)는 제공되지 않습니다.
클래스 상수에 접근하기
클래스 내에서의 접근
[code php;gutter:false] <?php
define('FATAL','Main Fatal error\n');

class ErrorCodes {
const FATAL = "Fatal error\n";

function print_fatal_error_codes() {
print "FATAL = " . FATAL;
}
}

/* Call the static function and move into the ErrorCodes scope */
ErrorCodes::print_fatal_error_codes();
?> [/code]
이 소스를 실행하면 클래스 ErrorCodes 내에 정의된 클래스상수 FATAL의 값이 출력될 것입니다.
'클래스명::클래스상수'를 이용한 접근
클래스명과 범위연산자(scope resolver) '::'를 이용하면 클래스 외부에서도 클래스 상수에 쉽게 접근할 수 있습니다.
[code php;gutter:false] <?php
class foo {
const hey = 'hello';

function printHey() {
print hey;
}
}

print foo::hey;
foo::printHey();
?> [/code]
접근자 self, parent, main 및 범위연산자 '::'를 이용한 접근
범위연산자 '::'와 'self', 'parent', 'main' 키워드를 이용하여 서로 다른 스코프를 갖는 상수에 접근하여 보면 아래와 같습니다.
[code php;gutter:false] <?php
define('FATAL','Main Fatal error\n');

class ErrorBase {
const FATAL = "Base Fatal error\n";
}

class ErrorCodes extends ErrorBase {
const FATAL = "Fatal error\n";

function print_fatal_error_codes() {
print "FATAL = " . FATAL;
print "self::FATAL = " . self::FATAL;
print "parent::FATAL = " . parent::FATAL;
print "main::FATAL = " . main::FATAL;
}
}

/* Call the static function and move into the ErrorCodes scope */
ErrorCodes::print_fatal_error_codes();
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:19
클래스 안의 클래스(Nested class)
클래스 안의 클래스는 클래스를 포함한 클래스를 의미하며 포함하고 있는 외부클래스와 포함되어진 내부클래스로 나누어 집니다.
내부클래스는 다른 클래스에 들어 있는 클래스입니다. 내부클래스를 정의하는 것은 클래스 이름을 숨기고 클래스에 필요한 자원의 지역적 할당을 가능하게 해 줍니다. 클래스를 별도로 작성하기에는 그 기능이 제한적이거나 외부 클래스에 종속적일 때 사용할 수 있으며 그래서 같은 일을 하는 클래스들끼리 기능적으로 묶는 역할을 하는데 이용할 수 있습니다.
 아래의 예를 보면 클래스 Database 내부에 데이터베이스 접속을 담당하는 내부클래스가 각 데이터베이스별로 작성되어 있습니다.
[code php;gutter:false] <?php
class Database {
class MySQL {
var $host = "";

function db_connect($user) {
print "Connecting to MySQL database '$this->host' as $user\n";
}
}

class Oracle {
var $host = "localhost";

function db_connect($user) {
print "Connecting to Oracle database '$this->host' as $user\n";
}
}
}

$MySQL_obj = new Database::MySQL();
$MySQL_obj->db_connect("John");

$Oracle_obj = new Database::Oracle();
$Oracle_obj->db_connect("Mark");
?> [/code]
때에 따라서는 내부클래스를 외부클래스 안쪽에 정의하지 않고 별도로 빼내어 아래와 같이 범위연산자 '::'를 이용하여 작성할 수 있습니다. 이러한 방법으로 작성하게 되면 소스 코드가 좀더 깔끔하게 되어 가독성이 좋아질 것입니다.
[code php;gutter:false] <?php
class Database::MySQL {
var $host = "";

function db_connect($user) {
print "Connecting to MySQL database '$this->host' as $user\n";
}
}

class Database::Oracle {
var $host = "localhost";

function db_connect($user) {
print "Connecting to Oracle database '$this->host' as $user\n";
}
}

$MySQL_obj = new Database::MySQL();
$MySQL_obj->db_connect("John");

$Oracle_obj = new Database::Oracle();
$Oracle_obj->db_connect("Mark");
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:18
객체 삭제(Forced deletion of objects)
Zend 엔진 1.0에서는 스크립트의 실행이 종료되면 객체들도 자동적으로 제거되기 때문에 스크립트 종료 직전에는 객체를 삭제할 필요가 없습니다만 만약 스크립트를 실행하는 중간에 객체를 삭제할 필요가 있는 경우라면 보통 unset() 함수를 이용하여 객체를 메모리에서 제거하게 됩니다.
[code php;gutter:false] unset($object); [/code]
특정 객체에 대한 별도의 객체 참조가 존재하지 않는다면 unset() 함수를 이용하면 별문제없이 객체를 삭제할 수 있으나 만약 특정 객체에 대한 객체 참조가 존재한다면 이 문장은 제대로 동작하지 않습니다. 객체 참조가 존재하는 한  해당 객체를 강제로 삭제할 수가 없다는 것입니다.
Zend 엔진 2.0에서는 좀더 진보적(?)으로 객체를 삭제할 수 있는 delete 문을 제공합니다.
[code php;gutter:false] delete $object; [/code]
delete는 새롭게 추가된 예약어로써 만약 이전의 작성된 문서에서 사용자 정의 delete()문을 가지고 있다면 이 문서를 Zend 엔진 2.0로 읽혀진다면 parser 에러가 발생할 것입니다.
새롭게 추가된 delete 문은 우선 해당 객체의 소멸자를 호출한 후에 객체가 다른 곳에서 참조되고 있는 경우에도 해당 객체를 강제로 삭제시킵니다. 따라서 해당 객체에 대한 모든 참조는 delete 문에 의해 객체가 삭제되는 순간에 그 의미가 없어지게 됩니다. 만약 delete 문에 의해 삭제된 객체를 참조를 통해 접근한다면 Zend 엔진 2.0은 해당 문서에 대하여 fatal 에러를 발생시킵니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:16
객체 역참조(Object Dereferencing)
참조 변수를 통한 역참조
Zend 엔진 1.0이 탑재된 PHP 4.0.4 이상 버전에서의 객체 역참조는 참조 연산자 &로 정의된 참조 변수를 통해 구현할 수 있었습니다.
[code php;gutter:false] <?php
class test {
var $mb;

function test($mb="default") {
$this->mb = $mb;
}
}

$a = &new test; // 객체 참조
?> [/code]
위의 예제에서 '$a = &new test'를 수행하게 되면 클래스 test로부터 생성된 객체 원본에 대한 참조변수 $a를 정의하게 됩니다.
[code php;gutter:false] <?php
.
. 생략
.

$a = &new test; // 객체 참조
echo $a->mb; // 객체 역참조
?> [/code]
'$a->mb'와 같이 참조변수 $a를 통하여 test로부터 생성된 객체 원본의 모든 멤버에 접근할 수 있으며 이와 같이 객체 참조를 통해서 역으로 객체 멤버에 접근하는 과정을 역참조(dereference)라 합니다.
Zend 엔진 2.0부터는 자바와 같이 객체가 참조로 전달되기 때문에 참조 연산자 &를 명기하지 않습니다. 위의 역참조 소스를 Zend 엔진 2.0이 탑재된 PHP 4 CVS (4.3.0-dev)용으로 재작성한다면 아래와 같게 되겠지요.
[code php;gutter:false] <?php
class test {
var $mb;

function test($mb="default") {
$this->mb = $mb;
}
}

$a = new test; // 객체 참조
echo $a->mb; // 객체 역참조
?> [/code]
Zend 엔진 2.0에서의 객체 역참조
Zend 엔진 2.0에서는 참조변수가 아닌 함수 또는 메소드로부터 반환된 객체를 통해 역참조할 수 있습니다. 이때 함수 또는 메소드로부터 반환된 객체는 당연히 참조가 되겠지요.
함수로부터 반환된 객체의 역참조(dereferencing objects returned from functions)
우선 Zend 엔진 1.0이 탑재된 PHP 4.0.4 이상 버전에서 함수로부터 반환된 객체를 역참조하는 예를 들어보지요.
[code php;gutter:false] <?php
class Circle {
function draw() {
print "Circle\n";
}
}

class Square {
function draw() {
print "Square\n";
}
}

function &ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}

$obj = &ShapeFactoryMethod("Circle"); // 함수로부터 반환된 객체 참조
$obj->draw(); // 객체 역참조
$obj = &ShapeFactoryMethod("Square") // 함수로부터 반환된 객체 참조
$obj->draw(); // 객체 역참조
?> [/code]
Zend 엔진 1.0이 탑재된 PHP 4.0.4 이상 버전에서는 위의 예에서 보는 것과 같이 반환되는 변수의 타입이 객체일 때 객체 그 자체가 아닌 참조로 반환하기 위해서는 참조 연산자 &를 붙여야 합니다.
그러나 Zend 엔진 2.0이 탑재된 PHP 4 CVS (4.3.0-dev)에서는 반환되는 변수의 타입이 객체인 경우에 참조 연산자 &를 붙이지 않더라도 기본적으로 객체 참조가 반환됩니다.
또한 Zend 엔진 1.0과는 달리 반환된 객체 참조를 또 다른 참조 $obj를 거치지 않더러도 함수로부터 반환된 객체 참조를 가지고 바로 역참조할 수 있습니다.
아래는 Zend 엔진 2.0이 탑재된 PHP 4 CVS (4.3.0-dev)에서 함수로부터 반환된 객체 참조를 가지고 역참조하는 예를 보여줍니다.
[code php;gutter:false] <?php
class Circle {
function draw() {
print "Circle\n";
}
}

class Square {
function draw() {
print "Square\n";
}
}

function ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}

ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?> [/code]
정적 함수를 통한 또 다른 예를 보면 아래와 같습니다.
[code php;gutter:false] <?php
class Counter {
var $counter = 0;

function increment_and_print() {
print ++$this->counter;
print "\n";
}
}

class SingletonCounter {
static $m_instance = NULL;

function Instance() {
if (self::$m_instance == NULL) {
self::$m_instance = new Counter();
}
return self::$m_instance;
}
}

SingletonCounter::Instance()->increment_and_print();
SingletonCounter::Instance()->increment_and_print();
SingletonCounter::Instance()->increment_and_print();
?> [/code]
메소드로부터 반환된 객체의 다중 역참조(Multiple dereferencing of objects returned from methods)
아래의 예와 같이 역참조로 반환된 객체 참조를 가지고도 반복해서 역참조할 수 있습니다.
[code php;gutter:false] <?php
class Name {
function Name($_name) {
$this->name = $_name;
}

function display() {
print $this->name;
print "\n";
}
}

class Person {
function Person($_name, $_address) {
$this->name = new Name($_name);
}

function getName() {
return $this->name;
}
}

$person = new Person("John", "New York");
print $person->getName()->display();
?> [/code]
역참조된 멤버변수에 값 할당하기
역참조에 의해 메소드를 호출하거나 메소드의 값을 되돌려 받을 수 있음을 위에서 알아보았습니다. 역참조에 의해 접근된 멤버변수에 대하여도 값을 읽을 수도 있고 더 나아가 아래와 같이 값을 할당할 수도 있습니다.
[code php;gutter:false] <?php
class Name {
var $id;

function Name($_name) {
$this->name = $_name;
}

function display() {
print $this->name;
print "\n";
}
}

class Person {
function Person($_name, $_address) {
$this->name = new Name($_name);
}

function getName() {
return $this->name;
}
}

$person = new Person("John", "New York");
$person->getName()->id = "hwooky";
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:15
객체 복제(Object Cloning)
Zend 1.0 객체 모델에서는 객체를 다룰 때 기본적으로 값으로 다루도록 설계되었기 때문에 객체를 함수의 인자로 전달하거나 함수로부터 객체가 반환될 때 또 할당문을 이용하여 객체가 할당될 때 기본적으로 객체 복사가 됩니다. 복사하는 동안에 Zend 엔진 1.0은 객체 프로퍼티의 전부에 대한 동일한 복사본을 만들기 위하여 비트 단위 복사(bitwise copy)를 하였습니다.
그러나 이러한 복사를 기반으로 한 객체모델에서는 자원 이용의 비효율성과 실행 시간의 지연과 같은 문제를 근원적으로 떠안고 있을 수밖에 없었으며 객체지향 프로그래밍의 여러 특징을 구현하는데 한계를 가지게 되었습니다. 사실 대부분의 경우에서 개발자들은 완전히 복제된 프로퍼티를 가진 객체의 복사를 생성할 필요는 없었습니다.
Zend 2.0에서의 객체 복제
객체 참조
Zend 2.0 객체 모델에서는 객체를 다룰 때 값이 아닌 참조로 다루도록 개선되었습니다. 이러한 이유로 원본 객체로부터 할당된 객체 또는 함수에 의해 반환된 객체를 다루게 되면 원본 객체 그 자체를 다루게 되지요.
객체 복사
그런데 때로는 원본 객체를 원래 상태로 유지하여야 할 경우도 있을 것입니다. 이러한 경우에는 예전 Zend 엔진 1.0에서처럼 참조가 아닌 복사를 하여 복사된 객체를 가지고 작업을 하여야 합니다. Zend 엔진 1.0에서야 기본적으로 객체 참조가 아닌 객체 복사였기 때문에 전혀 문제가 되지 않았었지만 Zend 엔진 2.0에서는 기본적인 처리가 객체 참조가 되기 때문에 객체 복사를 위한 새로운 수단이 필요하게 되었지요.
Zend 엔진 2.0에서 객체 복사를 위한 수단이 바로 __clone() 메소드입니다. __clone 메소드를 호출함으로써 원본 객체를 복제한 새로운 객체가 생성되는 것이지요.
[code php;gutter:false] <?php
$copy_of_object = $object->__clone();
?> [/code]
객체 복사를 하기 위해서는 반드시 __clone() 메소드를 이용해야 합니다. __clone() 메소드를 이용하면 원본 객체를 복사한 동일한 객체를 만들 수 있으며 이 객체의 멤버를 수정하더라도 원본 객체는 원래 상태를 그대로 유지할 수가 있습니다.
__clone() 메소드의 사용자 정의
새로운 객체의 복사본을 생성하기 위하여 __clone() 메소드를 호출하게 되면 Zend 엔진 2.0은 클래스 내에 정의된 __clone() 메소드가 존재하는지 확인할 것입니다.
만약 정의된 __clone() 메소드가 없다면 Zend 엔진 2.0에 내장된 __clone() 메소드를 호출하게 되고 이 메소드에 의해 객체 프로퍼티 전체를 복사할 것입니다.
만약 정의된 __clone() 메소드가 있다면 생성된 객체에서 필요로하는 프로퍼티에 대하여는 정의된 __clone() 메소드 내에서 직접 설정해 주어야 합니다.
[code php;gutter:false] <?php
class MyCloneable {
static $id = 0;

function MyCloneable() {
$this->id = self::$id++;
}

function __clone() {
$this->name = $clone->name;
$this->address = 'New York';
$this->id = self::$id++;
}
}

$obj = new MyCloneable();

$obj->name = 'Hello';
$obj->address = 'Tel-Aviv';

print $obj->id . "\n";

$obj = $obj->__clone();

print $obj->id . "\n";
print $obj->name . "\n";
print $obj->address . "\n";
?> [/code]
자식클래스는 부모클래스의 __clone() 메소드를 오버라이드(override) 할 수 있습니다만, 그 경우 자식클래스에서 부모클래스의 __clone() 메소드를 호출하려면 아래와 같이 'parent' 키워드를 이용하여 참조합니다.
[code php;gutter:false] <?php
class SubCloneable extends SuperCloneable {
static $id = 0;

function SubCloneable() {
$this->id = self::$id++;
}

function __clone() {
$this = parent::__clone();
$this->name = $clone->name;
$this->address = 'New York';
$this->id = self::$id++;
}
}
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:14
소멸자(Destructors)
소멸자의 역할
생성된 객체가 소멸되기 직전에 자동으로 불려지는 함수이며 주로 객체에 관련된 메모리를 정리하기 위하여 수행됩니다.
소멸자의 동작
클래스가 상속관계에 있을 때에 각 클래스에 있는 소멸자의 실행순서는 생성자와 반대로 동작하게 됩니다. 즉, 파생클래스의 소멸자가 먼저 실행된 다음에 부모클래스의 소멸자가 순서대로 실행됩니다.
소멸자의 특징
인자와 리턴값이 없으며 상속되지 않습니다.
Zend 엔진 1.0에서의 소멸자
PHP3 또는 Zend 엔진 1.0이 탑재된 PHP4에서는 소멸자를 지원하지 않습니다. 따라서 객체가 소멸할 때 처리해야 할 것이 있다면 임시방편적으로 register_shutdown_function() 함수를 이용하였습니다. 그러나 register_shutdown_function() 함수는 현재 문서의 요청(request)가 종료되는 시점에서 불려지는 PHP 내장함수로 특정 객체의 소멸과는 관계없다는 것에 유의해야 할 것입니다.
Zend 엔진 2.0에서의 소멸자 지원
비록 Zend 엔진 1.0에서도 register_shutdown_function() 함수를 이용하여 소멸자와 유사한 일을 할 수 있었지만 클래스 내의 일은 클래스 내에서 처리한다는 캡슐화 개념하고는 거리가 먼 전역적인 작업이 될 수밖에 없었습니다.
Zend 엔진 2.0에서는 소멸자를 공식 지원하기 때문에 객체를 처리하는 능력이 한층 높아지게 되었습니다. 소멸자를 통하여 디버깅에 대한 메시지들을 기록할 수 있게 되었고, 데이터 베이스 연결들을 닫는 등의 관련작업을 깨끗이 마무리할 수 있게 되었습니다.
복사가 아닌 참조를 바탕으로 설계된 Zend 엔진 2.0에서는 객체의 마지막 참조가 소멸될 때 해당 객체가 메모리로부터 해제됩니다. 소멸자는 객체가 메모리로부터 해제되기 직전에 호출되어 수행되며 소멸자명은 __destruct()으로 단일화되어 있으며 인자없이 호출됩니다.
[code php;gutter:false] <?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = 'MyDestructableClass';
}

function __destruct() {
print'Destroying'. $this->name. "\n";
}
}

$obj = new MyDestructableClass();
?> [/code]
생성자와는 다르게 부모클래스의 소멸자가 자동적으로 호출되지는 않습니다. 부모클래스의 소멸자를 실행하기 위해서는 아래의 예와 같이 명시적으로 부모클래스의 소멸자를 호출하여야 합니다.
[code php;gutter:false] <?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

class ChildDestructableClass {
function __destruct() {
parent::__destruct();
}
}

$obj = new ChildDestructableClass();
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:12
단일화된 생성자(Unified Constructors)
생성자명
생성자(constructor)는 'new' 키워드에 의하여 클래스의 새로운 인스턴스가 생성될 때 자동적으로 호출되는 클래스 내의 특별한 메소드이며, 객체의 다른 멤버가 사용되기 전에 필요로 하는 초기화같은 작업을 생성자를 통하여 수행하게 됩니다.
PHP3 및 Zend 엔진 1.0에서의 생성자는 클래스명과 동일한 이름을 가지게 됩니다. 아래와 같이 클래스명과 동일한 이름의 메소드가 존재한다면 이것을 자동적으로 생성자로 취급합니다.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code

}

};
?> [/code]
Zend 엔진 2.0에서의 생성자는 클래스명과 관계없이 단순히 __construct()라는 이름으로 단일화된 생성자를 호출할 수 있습니다.
[code php;gutter:false] <?php
class Shape {
function __construct() {
// shape initialization code

}

};
?> [/code]
생성자명을 __construct()로 단일화한 이유
PHP3 및 Zend 엔진 1.0에서 생성자명으로 생기는 문제
클래스의 상속관계에 변화가 생겨서 수정하는 경우를 생각해 보세요. 파생한 클래스로부터 부모클래스의 생성자를 호출하는 것은 흔히 있는 일이기 때문에 Zend 엔진 1.0에서 수행하던 방법에 의하여 작성된 상속관계에 변화가 생겨 클래스를 이동하려면 약간의 수고가 필요할 것입니다.
수정하기 전에 작성된 초기의 클래스 상속관계가 아래와 같다고 예를 들어보지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code

}

};

class Square extends Shape {
function Square() {
parent::Shape();
// square-specific initialization code

}

};
?> [/code]
어떠한 이유에 의하여 클래스 Square와 Shape 사이에 새로운 클래스 Rectangle가 삽입되어 상속관계에 변화가 생겼다고 가정해 보지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code

}

};

class Rectangle extends Shape {
function Rectangle() {
parent::Shape();
// rectangle initialization code
}

};

class Square extends Rectangle {
function Square() {
parent::Rectangle();
// square-specific initialization code

}

};
?> [/code]
수정전과 후의 코드를 비교해 보았을 때 클래스 Square의 생성자 내에서 부모클래스를 호출하는 부분에 변화가 생긴 것을 볼 수 있습니다. 원래는 클래스 Square의 부모클래스가 Shape였으나 클래스 Square와 Shape 사이에 새로운 클래스 Rectangle가 삽입되었기 때문에 클래스 Square의 부모클래스가 Rectangle로 바뀌었습니다. 이에 따라 부모클래스를 호출하는 부분이 parent::Shape()에서 parent::Rectangle()로 수정되었습니다.
상속관계에 변화가 생길 때마다 생성자 내용을 일일이 수정해야 한다는 것은 여간 신경쓰이는 일이 아닙니다. 문서의 규모가 커지고 상속관계가 복잡해 진다면 다소 부담스러울 수도 있겠지요.
이러한 이유로 Zend 엔진 2.0에서의 생성자는 클래스명과 관계없이 단순히 __construct()라는 이름으로 생성자를 호출할 수 있도록 개선되었습니다.
Zend 엔진 2.0에서 단일화된 생성자
위의 예를 Zend 엔진 2.0에서는 어떻게 처리되는지 살펴보겠습니다. Zend 엔진 2.0으로 작성된 수정하기 전의 초기의 클래스 상속관계는 아래와 같을 것입니다.
[code php;gutter:false] <?php
class Shape {
function __construct() {
// shape initialization code

}

};

class Square extends Shape {
function __construct() {
parent::__construct();
// square-specific initialization code

}

};
?> [/code]
클래스 Square와 Shape 사이에 새로운 클래스 Rectangle가 삽입되어 상속관계에 변화가 생겼을 때의 수정된 소스는 아래와 같겠지요.
[code php;gutter:false] <?php
class Shape {
function __construct() {
// shape initialization code

}

};

class Rectangle extends Shape {
function __construct() {
parent::__construct();
// rectangle initialization code
}

};

class Square extends Rectangle {
function __construct() {
parent::__construct();
// square-specific initialization code

}

};
?> [/code]
Zend 엔진 1.0으로 작성된 소스와는 달리 수정전과 후의 코드를 비교해 보았을 때 클래스 Square의 생성자 내에서 부모클래스를 호출하는 부분에 변화가 전혀 없음을 볼 수 있습니다.
Zend 엔진 2.0에서는 생성자명을 클래스명 대신에 '__construct()'라는 이름으로 호출할 수 있도록 생성자 선언 방법을 표준화함으로써 클래스의 상속관계에 변화가 생겼을 때에 생성자에서 부모클래스를 호출하는 부분을 일일이 수정해야 할 필요가 없어졌습니다. 이것이 생성자를 __construct()로 단일화했을 때에 얻을 수 있는 큰 이점이라 할 수 있지요.
Zend 엔진 2.0에서의 생성자 호출에 관한 하위호환성
생성자 호출 메카니즘
만약 Zend 엔진 2.0이 정의된 클래스에 __construct()를 발견할 수 없으면 하위 호환성을 가지기 위하여, Zend 엔진 1.0에서의 생성자, 즉 클래스명과 동일한 이름의 메소드를 찾을 것입니다.
따라서 아래와 같이 Zend 엔진 1.0용으로 작성된 소스를 Zend 엔진 2.0에서 수행하더라도 정상적으로 동작할 것입니다.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0

}

};
?> [/code]
같은 이유로 아래와 같이 Zend 엔진 1.0용 생성자와 Zend 엔진 2.0용 생성자가 모두 정의되어 있다면 Zend 엔진 1.0용 생성자는 무시되고 Zend 엔진 2.0용 생성자 __construct() 만 호출되겠지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0

}

function __construct() {
// shape initialization code for Zend Engine 2.0

}

};
?> [/code]
Zend 엔진 1.0 및 Zend 엔진 2.0 에서 모두 동작하는 생성자
Zend 엔진 1.0이 탑재된 PHP4에서 Zend 엔진 2.0과의 호환성을 가지도록 생성자를 정의한다면 아래와 같이 작성하면 가능하니라 생각합니다.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0
$this->__construct();
}

function __construct() {
// shape initialization code for Zend Engine 2.0

}

};

$obj = new Shape();
?> [/code]
위의 소스는 Zend 엔진 1.0과 2.0에서 모두 동일한 결과를 얻게 될 것입니다. Zend 엔진 1.0을 더 이상 필요없다면 Zend 엔진 1.0용 생성자인 Shape()을 완전히 삭제하면 되겠지요.
Zend 엔진 1.0용 생성자 Shape()에서 $this 객체를 이용하여 Zend 엔진 2.0용 생성자를 호출하기 때문에 static 멤버로는 접근할 수 없으며 반드시 인스턴스화한 객체를 가지고 접근할 때만 정상적으로 동작됩니다. 물론 생성자라는 것이 인스턴스화할 때만 동작하는 특별한 메소드이기 때문에 별 문제는 되지 않지만 좀더 소스 코드를 명료하게 작성한다면 이를 아래와 같이 $this 객체 대신에 클래스명과 범위연산자를 이용하면 될 것입니다. 수정된 코드에서의 단점이라면 현재 클래스명 Shape가 변경된다면 Shape::__construct() 부분도 함께 수정되어야 한다는 것이지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0
Shape::__construct();
}

function __construct() {
// shape initialization code for Zend Engine 2.0

}

};

$obj = new Shape();
?> [/code]

Posted by 방글24