정적 데이터멤버란?
생성된 모든 인스턴스들이 공유하는 공통적인 정보가 필요할 때에 사용되는 것이 정적 데이터멤버(static data member)입니다. 정적 데이터멤버는 다른 일반 데이터 멤버와 달리 각각의 인스턴스에 기억장소가 할당되는 것이 아니라, 단 한 개의 기억장소만이 할당되고 모든 인스턴스에 의하여 공유됩니다. 하지만 데이터 멤버의 정보는 각각의 인스턴스가 모두 가지고 있는 것처럼 간주됩니다. 이와 같이 모든 인스턴스에서 공유하는 데이터 멤버를 정적 데이터 멤버로 선언하므로 써, 모든 인스턴스에서 중복되는 기억장소를 절약하는 효과를 얻을 수 있습니다. 때에 따라서는 동일한 클래스 또는 이를 상속한 클래스를 이용해 몇 개의 인스턴스를 생성하더라도 동일한 데이터로 취급해야 할 데이터멤버가 때로는 필요하지요.
PHP3, PHP4 모두 정적 데이터멤버를 지원하지 않습니다.
현재 PHP 스크립트로 정적 데이터멤버의 기능을 구현하려면 전역변수를 이용하여야 합니다. PHP에서는 정적 데이터멤버를 지원하지 않기 때문에 이를 표현할 수 없지만 static 키워드로 표현할 수 있다고 가정하고 예제소스를 작성해 보겠습니다.
[code php;gutter:false]
class cls경리부 {
static $수입금액=0;
// php에서 static은 정적변수를 나타내는 키워드이나
// 여기서는 정적 데이터멤버를 나타낸다고 가정하였음
function add_수입금액($income) {
$this->수입금액 += $income;
}
}
class cls판매부 extends cls경리부 {
var $판매금액;
function add_판매금액($income) {
$this->판매금액 += $income;
$this->add_수입금액($income);
}
}
$화장품판매부 = new cls판매부;
$자동차판매부 = new cls판매부;
$화장품판매부->add_판매금액(40000);
$자동차판매부->add_판매금액(20000);
[/code]
동일한 클래스 cls판매부를 이용하여 $화장품판매부와 $자동차판매부에 대한 객체를 별도로 생성합니다. 부모클래스인 cls경리부의 데이터멤버 $수입금액은 모든 객체에서 공용으로 사용하려는 데이터멤버입니다. "$화장품판매부->판매금액"과 "$자동차판매부->판매금액"은 별도의 인스턴스로 처리되어 독립적으로 관리되어지나 경리부에서 집계하는 수입금액은 각 판매부에서 판매된 판매금액이 누적되는 값으로 모든 인스턴스가 공유해야 하는 데이터입니다. 이럴 때 수입금액을 정적 데이터멤버로 정의하여 사용하면 편리하지요.
위의 예에서 실행결과는(진짜 실행되는 것은 아니지만 실행된다면), 자동차판매부의 데이터멤버 $판매금액에는 "20,000"원이 저장되고, 화장품판매부의 데이터멤버 $판매금액에는 "40,000"원이 저장되고, 경리부에 최종 집계되는 데이터멤버 $수입금액에는 "60,000"원이 저장됩니다.
정적 데이터멤버를 흉내내기
PHP 에서는 수입금액과 같이 모든 인스턴스에서 공유해야하는 정적 데이터멤버를 정의할 수 없습니다. 따라서 클래스에서 정적 데이터멤버를 흉내내기 위한 기술이 필요합니다. 방법이야 아이디어에 따라 여러가지가 있겠지만 현재까지 제가 알고 있는 방법에는 아래와 같이 네 가지 정도가 있습니다.
1. 후키라이브러리에서처럼 별도의 파서를 라이브러리화하여 사용하는 방법
2. 전역변수를 이용하는 방법
3. 정적변수를 이용하는 방법
4. 클래스를 이용하는 방법
5. 객체 및 정적변수를 이용하는 방법(가장 개선된 방법)
첫번째 방법은 제가 공개한 후키라이브러리를 참고하시기 바랍니다. 사실 첫번째 방법은 1차적으로 파서가 동작된다뿐이지 2차적으로는 두번째, 세번째 또는 네번째 방법을 이용하여 정적 데이터멤버를 구현합니다.
전역변수를 이용하여 정적 데이터멤버를 흉내내기
전역변수를 이용하면 다루기는 무척 간단하고 쉽습니다. 문제는 객체지향프로그래밍의 장점인 정보은닉(information hiding)가 전혀 이루어질 수 없다는 것입니다. 즉, 전역변수(명)를 잘 관리하지 않으면 페이지 내의 다른 부분(전역변수)과 충돌을 일으킬 수 있습니다. 이 것만 주의한다면 다루기에는 무지 편합니다. 위의 예제소스를 전역변수를 이용하여 실제 PHP에서 실행할 수 있는 코드로 바꾸어 보겠습니다.
[code php;gutter:false]
$수입금액 = 0; // 전역변수
class cls경리부 {
function add_수입금액($income) {
$GLOBALS[수입금액] += $income;
}
}
class cls판매부 extends cls경리부 {
var $판매금액;
function add_판매금액($income) {
$this->판매금액 += $income;
$this->add_수입금액($income);
}
}
$화장품판매부 = new cls판매부;
$자동차판매부 = new cls판매부;
$화장품판매부->add_판매금액(40000);
$자동차판매부->add_판매금액(20000);
[/code]
여기서는 "cls경리부" 클래스만 수정하면 되는데, 우선 "static $수입금액=0;" 대신에 클래스 정의 외부(main body 부분)에 $수입금액을 전역변수로 선언해준 후 전역변수로써 $수입금액을 다루면 됩니다. 비교적 간단하지만 위에서 언급했듯이 "$수입금액"이라는 전역변수를 다른 곳에서 사용하지 말라는 법이 없으니까 변수명을 정할 때 주의하셔야 합니다.
정적변수를 통해 정적데이터멤버를 흉내내기
정적 데이터멤버와 같이 모든 객체가 공유할 수 있는 변수를 사용하려면 현재는 전역변수를 이용할 수 밖에 없는데, 만약 모든 객체가 공유할 변수가 하나밖에 없다면 위와 같이 메소드를 작성하여 이 메소드를 통해 변수를 다루어도 될 것 같습니다. OOP에서 전역변수 사용에 알레르기가 있다면 PHP에서 공식적으로 정적 데이터멤버를 지원할 때까지는 차선책으로 이 방법도 괜찮을 것 같네요. 아래는 사용하는 예입니다.
[code php;gutter:false]
<?php
class test {
function statics($val="") {
static $stat;
if ($val)
$stat = $val;
else
echo "var[$stat]\n";
}
}
$obj1 = new test;
$obj2 = new test;
$obj1->statics("obj1에서 기록함");
$obj2->statics();
$obj2->statics("obj2에서 기록함");
$obj1->statics();
?>
[/code]
< 소스 코드(test.php3) >
var[obj1에서 기록함]
var[obj2에서 기록함]
< 실행결과 >
이 방법은 PHP4에서만 가능합니다. 그리고 PHP4에서도 동일한 클래스명으로 객체화하였을 때만 제대로 동작됩니다. 만약 동일할 클래스를 상속받은 다른 클래스명으로 객체화 한 경우에는 제대로 동작되지 않습니다.
PHP3 에서 PHP4로 수정하면서 static의 문제(사실은 클래스 문제였다고 할 수 있음)를 손본것 같은데 완벽하게는 손보지 못한 것 같네요. 즉, 멤버함수는 정적 데이터멤버와 같이 객체가 아닌 클래스에 속하여서 작동하게 되는데 상속된 클래스에서 객체화 하는 경우에는 이 부분의 처리가 좀 미숙한 것 같네요. 아래가 이러한 경우의 예입니다. 보시면 obj3의 멤버함수와 obj1,obj2의 멤버함수 사이의 관계가 좀 별다른 것 같네요. 아니면 멤버함수는 동일하게 사용하는데 그 안에 있는 정적변수만 다르게 처리하던지요. 아무튼 이 부분만 주의하여 사용하시기 바랍니다.
[code php;gutter:false]
<?php
class test {
function statics($val="") {
static $stat;
if ($val)
$stat = $val;
else
echo "var[$stat]\n";
}
}
class test2 extends test {
}
$obj1 = new test;
$obj2 = new test;
$obj3 = new test2;
$obj1->statics("obj1에서 기록함");
$obj2->statics();
$obj3->statics();
$obj2->statics("obj2에서 기록함");
$obj1->statics();
$obj3->statics();
$obj3->statics("obj3에서 기록함");
$obj1->statics();
$obj2->statics();
[/code]
< 소스 코드(test.php3) >
var[obj1에서 기록함]
var[]
var[obj2에서 기록함]
var[]
var[obj2에서 기록함]
var[obj2에서 기록함]
< 실행결과 >
클래스를 이용하여 정적 데이터멤버를 흉내내기(1)
static 키워드를 사용할 수 없기 때문에 직관적으로 정적 데이터멤버를 다룰 수는 없고 또 다소 복잡한 면이 있지만 객체지향프로그래밍의 핵심 기술인 정보은폐를 어느 정도 보상받을 수 있다는 면에서 추천할 만한 방법이 되겠습니다.
[code php;gutter:false]
<?php
class cls경리부 {
var $수입금액 = 0;
function add_수입금액($income) {
$this->수입금액 += $income;
}
}
class cls판매부 {
var $판매금액;
var $objname;
function cls판매부($objname="") {
global $$objname;
if (!is_object($$objname) && $objname) {
$$objname = new cls경리부;
}
$this->objname = $objname;
}
function add_판매금액($income) {
$this->판매금액 += $income;
$GLOBALS[$this->objname]->add_수입금액($income);
}
}
$화장품판매부 = new cls판매부("경리부");
$자동차판매부 = new cls판매부("경리부");
$화장품판매부->add_판매금액(40000);
$자동차판매부->add_판매금액(20000);
echo "각 판매부별 판매금액 누계\n";
echo "-------------------------\n";
echo "화장품판매부 : $화장품판매부->판매금액"."원\n";
echo "자동차판매부 : $자동차판매부->판매금액"."원\n";
echo "\n";
echo "총 판매금액 누계\n";
echo "----------------\n";
echo "전 부서 판매금액 : $경리부->수입금액"."원\n";
?>
[/code]
< 소스 코드(test.php3) >
각 판매부별 판매금액 누계
-------------------------
화장품판매부 : 40000원
자동차판매부 : 20000원
총 판매금액 누계
----------------
전 부서 판매금액 : 60000원
< 실행결과 >
가장 핵심적인 부분이 cls판매부의 생성자입니다. 생성자에서 정적 데이터멤버를 흉내낼 수 있는 기반을 닦아 놓지요. 생성자가 인수로 입력받는 것이 정적 데이터멤버(?)가 있는 클래스를 객체 생성할 객체의 변수명입니다. 클래스 내에서 정적 데이터멤버(?)에 접근하는 방법은 아래와 같습니다.
[code php;gutter:false]
$GLOBALS[$this->objname]->add_수입금액($income);
$GLOBALS[$this->objname]->수입금액;
[/code]
클래스를 이용하여 정적 데이터멤버를 흉내내기(2)
위의 방법보다 훨씬 직관적이고 단순하며 의미가 명료하게 정적 데이터멤버를 흉내내는 방법입니다.
[code php;gutter:false]
<?php
class cls경리부 {}
class cls판매부 {
var $경리부;
var $판매금액;
var $objname;
function cls판매부($objname="") {
$this->경리부 = & new cls경리부;
}
function add_판매금액($income) {
$this->판매금액 += $income;
$this->경리부->수입금액 += $income;
}
}
$화장품판매부 = new cls판매부("경리부");
$자동차판매부 = new cls판매부("경리부");
$화장품판매부->add_판매금액(40000);
$자동차판매부->add_판매금액(20000);
echo "각 판매부별 판매금액 누계\n";
echo "-------------------------\n";
echo "화장품판매부 : $화장품판매부->판매금액"."원\n";
echo "자동차판매부 : $자동차판매부->판매금액"."원\n";
echo "\n";
echo "총 판매금액 누계\n";
echo "----------------\n";
echo "전 부서 판매금액 : $화장품판매부->경리부->수입금액"."원\n";
?>
[/code]
< 소스 코드(test.php3) >
클래스 cls판매부의 생성자에서 경리부를 정적 데이터멤버(또는 정적 멤버함수)로 처리하였기 때문에 마지막 문장에서 "$화장품판매부"를 통해도 되고 아니면 아래와 같이 "$자동차판매부"를 통해도 됩니다.
[code php;gutter:false]
echo "전 부서 판매금액 : $자동차판매부->경리부->수입금액"."원\n";
[/code]
객체 및 정적변수를 이용하여 정적데이터멤버를 흉내내기
정적변수만을 이용하는 방법에서의 문제점은 최하위 상속 클래스가 다를 때 정적변수가 제대로 동작하지 않는다는 것입니다. 이 문제점을 개선하기 위하여 최상위 클래스에 정적변수를 설치하지 않고 별도의 클래스로 정의된 곳에 정적변수를 설치하는 것입니다.
[code php;gutter:false]
<?php
/*
실험환경:PHP 4.1.1 (Windows 98)
*/
class hStatic {
function hStatic(&$obj) {
static $static; // 정적 변수 설치
$obj->STATIC = & $static; // 객체에서 사용한 정적 데이터 멤버 설치
}
}
class hTest {
function hTest() {
new hStatic($this); // 정적 데이터 멤버를 위한 클래스의 객체화
}
}
class hTestExt extends hTest {
function hTestExt() {
$this->hTest(); // 부모 클래스의 생성자 실행
}
}
class hTestExt2 extends hTestExt {
function hTestExt2() {
$this->hTestExt(); // 부모 클래스의 생성자 실행
}
}
$static1 = & new hTest;
$static2 = & new hTest;
$static3 = & new hTestExt;
$static4 = & new hTestExt2;
$static1->STATIC->MEMBER1 .= "[hTest-MEMBER1]static1's STATIC\n";
$static2->STATIC->MEMBER1 .= "[hTest-MEMBER1]static2's STATIC\n";
$static3->STATIC->MEMBER1 .= "[hTestExt-MEMBER1]static3's STATIC\n";
$static4->STATIC->MEMBER1 .= "[hTestExt2-MEMBER1]static4's STATIC\n";
$static1->STATIC->MEMBER2 .= "[hTest-MEMBER2]static1's STATIC\n";
$static2->STATIC->MEMBER2 .= "[hTest-MEMBER2]static2's STATIC\n";
$static3->STATIC->MEMBER2 .= "[hTestExt-MEMBER2]static3's STATIC\n";
$static4->STATIC->MEMBER2 .= "[hTestExt2-MEMBER2]static4's STATIC\n";
//echo $static1->STATIC->MEMBER1;
echo $static2->STATIC->MEMBER1;
//echo $static3->STATIC->MEMBER1;
//echo $static4->STATIC->MEMBER1;
//echo $static1->STATIC->MEMBER2;
//echo $static2->STATIC->MEMBER2;
echo $static3->STATIC->MEMBER2;
//echo $static4->STATIC->MEMBER2;
?>
[/code]
< 소스 코드(test.php3) >
[hTest-MEMBER1]static1's STATIC
[hTest-MEMBER1]static2's STATIC
[hTestExt-MEMBER1]static3's STATIC
[hTestExt2-MEMBER1]static4's STATIC
[hTest-MEMBER2]static1's STATIC
[hTest-MEMBER2]static2's STATIC
[hTestExt-MEMBER2]static3's STATIC
[hTestExt2-MEMBER2]static4's STATIC
< 실행결과 >
이 방법은 PHP4에서만 가능합니다. 이를 통해 객체지향 프로그래밍에서 요구하는 정적 데이터멤버를 별어려움 없이 흉내낼 수 있을 것입니다.