phpclass/객체모델2005. 10. 11. 12:23
written: Nov 07 2007
정적 멤버(static member)
PHP5에서 정적 멤버를 static 키워드를 이용하여 정의하여 사용할 수 있습니다.
visibility static $변수이름 = 변수값;
visibility static function 함수이름(인수리스트) { 함수내용 };
visibility 위치에는 정적 멤버의 가시범위(visibility)를 제한할 수 있는 PPP 접근제한자(private/protected/public access modifier)를 지정할 수 있습니다. 생략하면 PHP4와의 호환성을 고려하여 멤버를 public로 처리합니다.
정적 멤버는 객체를 생성하지 않고도 접근할 수 있도록 해줍니다.
[code php;gutter:false] <?php class Foo { public static $my_static = 'foo'; public function staticValue() { return self::$my_static; } } class Bar extends Foo { public function fooStatic() { return parent::$my_static; } } print Foo::$my_static . "\n"; $foo = new Foo(); print $foo->staticValue() . "\n"; print Bar::$my_static . "\n"; $bar = new Bar(); print $bar->fooStatic() . "\n"; ?> [/code]
< 예제출처:php.net >
정적 멤버 변수(static member variable)
[code php;gutter:true] <?php class test_class { public static $static_var = "my static variable's value\n"; public function get_static() { return self::$static_var; } public function set_static($val) { self::$static_var = $val . "\n"; } } print test_class::$static_var; $obj = new test_class(); $obj->set_static("my static variable's new value"); $obj2 = new test_class(); print $obj2->get_static(); ?> [/code]
위에서 볼 수 있듯이 정적 멤버 변수로 선언된 변수는 어느 인스턴스에서 수정되어도 모든 인스턴스에 적용이 됩니다. 위에서 17번째 줄에 보이듯이 $obj 인스턴스에 의해 수정된 정적 멤버 변수는 $obj2의 다른 인스턴스에도 모두 영향을 미칩니다.
출력 결과는 아래와 같습니다.
my static variable's value
my static variable's new value
클래스 상수와 마찬가지로 정적 멤버 변수는 객체를 통해서 접근할 수 없습니다. 이것은 정적 멤버 함수가 객체를 통해서 접근할 수 있는 것과 다르다는 점에 주의해야 합니다. 따라서 클래스 내부에서만 사용되는 $this 키워드를 이용해서도 접근이 불가능하며 반드시 범위지정연산자(::) 앞에 클래스명, self 또는 parent 키워드를 이용해서 접근해야만 합니다.
[code php;gutter:true] <?php class test_class { public static $static_var = "my static variable's value\n"; } $obj = new test_class(); print $obj->static_var; // Notice: Undefined property: test_class::$static_var in xxx.php on line 8 // $obj::static_var is not possible // Parse error: parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM // in xxx.php on line nnn ?> [/code]
정적 멤버 함수(static method)
정적 멤버 함수는 객체를 생성하지 않고도 접근할 수 있도록 해줍니다.
[code php;gutter:false] <?php class my_class { public static function hello_world() { print "Hello, world"; } } my_class::hello_world(); ?> [/code]
생성된 객체를 통해서 멤버에 접근할 수 있습니다.
[code php;gutter:false] <?php class my_class { public static function hello_world() { print "Hello, world"; } } $obj = new my_class; $obj->hello_world(); ?> [/code]
그러나 객체 생성없이 접근하는 경우도 있기 때문에 의사 변수(pseudo variable) $this를 정적 멤버 함수 내에서 사용해서는 안됩니다.
[code php;gutter:false] <?php class my_class { public static function hello_world() { print "Hello, world"; } public static function hello() { $this->hello_world(); } } $obj = new my_class; $obj->hello(); // Fatal error: Using $this when not in object context in xxx.php on line 8 ?> [/code]
정적멤버가 아닌 메쏘드를 정적으로 호출하는 것은 E_STRICT 레벨의 경고를 발생시킵니다.
[code php;gutter:false] <?php class my_class { public function hello_world() { print "Hello, world"; } } error_reporting(E_ALL | (defined('E_STRICT')? E_STRICT : 0)); my_class::hello_world(); // Strict Standards: Non-static method my_class::hello_public() // should not be called statically in xxx.php on line 10 ?> [/code]
싱글턴 패턴(singleton pattern)
정적 멤버 변수를 가장 유용하게 활용하는 곳이 있다면 디자인 패턴 중 싱글턴 패턴(singleton pattern)일 것입니다.
싱글턴 패턴은 프로그램이 동작할 때 클래스의 인스턴스가 반드시 하나만 존재하도록 해주는 패턴으로 매우 중요한 요소입니다만 PHP4에서는 정적멤버, PPP 접근제한자, 인터페이스 등의 객체지향언어의 핵심기능을 빠진 상태에서 구현이 사실상 불가능하였습니다.
PHP5부터 제공되기 시작한 정적멤버 및 PPP 접근제한자 등의 특성을 이용하여 디자인 패턴 중에서도 가장 널리 사용되는 싱글턴 패턴을 구현해 보겠습니다. 소스는 php.net의 document에서 발췌하여 수정한 것입니다.
[code php;gutter:false] <?php class Example { // Hold an instance of the class private static $instance; // Prevents direct creation of object private function __construct() { echo 'I am constructed'; } // The singleton method public static function singleton() { if (!isset(self::$instance)) { $c = __CLASS__; self::$instance = new $c; } return self::$instance; } // Example method public function bark() { echo 'Woof!'; } // Prevent users to clone the instance private function __clone() { } } //This allows a single instance of the Example class to be retrieved. // This will always retrieve a single instance of the class $test = Example::singleton(); $test->bark(); ?> [/code]
클래스 밖에서 singleton() 메쏘드를 이용하지 않고 아래 코드와 같이 new 또는 clone 연산자를 이용하여 객체를 생성하려면 Fatal 에러가 발생하기 때문에 프로그램상에서 인스턴스는 단 1개만 존재할 수 있습니다.
[code php;gutter:false] <?php class Example { // Hold an instance of the class private static $instance; // Prevents direct creation of object private function __construct() { echo 'I am constructed'; } // The singleton method public static function singleton() { if (!isset(self::$instance)) { $c = __CLASS__; self::$instance = new $c; } return self::$instance; } // Example method public function bark() { echo 'Woof!'; } // Prevent users to clone the instance private function __clone() { } } // This would fail because the constructor is private $test = new Example; // Fatal error: Call to private Example::__construct() // from invalid context in xxx.php on line 30 // This will issue an E_USER_ERROR. $test_clone = clone $test; // Fatal error: Call to private Example::__clone() from context '' // in xxx.php on line 35 ?> [/code]
다중쓰레드(multi thread)를 지원하지 않는 PHP에서야 별 상관없는 이야기이지만 자바와 같이 다중쓰레드를 지원하는 프로그램에서는 위의 코드도 완벽하지 않습니다. 여러 개의 쓰레드가 거의 동시에 singleton() 메쏘드를 호출한다고 가정한다면 순간적으로 여러 개의 인스턴스가 생성될 수 있습니다. 따라서 singleton() 메쏘드는 단 하나의 쓰레드만 접근할 수 있도록 제한할 필요가 있습니다. 이를 위한 연산자가 synchronized입니다. 다음은 "Java 언어로 배우는 디자인 패턴 입문 - (주)영진닷컴" p.445에 나오는 synchronized 연산자를 적용한 싱글턴 페턴 클래스 예제입니다.
[code java;gutter:false] public class Singleton { private static Singleton singleton = null; private Singleton() { System.out.println("인스턴스를 생성했습니다"); slowdown(); } public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } private void slowdown() { try { Thread.sleep(1000); } catch (InterruptedException e) { } } } [/code]
위 소스에서 slowdown() 메쏘드는 다중쓰레드에 의해 여러 개의 인스턴스가 생성되는지 확인하기 위해 프로그램의 동작 속도를 일부로 느리게 하기 위해 첨가된 코드입니다.

Posted by 방글24