phpclass/객체정보2008. 8. 11. 12:37
작성: 2008.08.11
이름지정규칙(Name resolution rules)
네임스페이스 지원으로 이름(클래스명, 함수명 등)은 동일하지만 서로 다른 네임스페이스에 소속될 수 있기 때문에 각 이름이 소속된 네임스페이스를 명확히 지정할 필요가 있습니다. 이름을 지정하는 방법으로는 크게 4가지로 나눌 수 있습니다.
▶ 다른 네임스페이스에 있는 이름을 참조할 때는 Fully Qualified Name 사용
▶ 현재 네임스페이스에 있는 이름을 참조할 때는 Unqualified Name 사용
▶ use 연산자를 이용하여 다른 네임스페이스를 현재 네임스페이스로 가져옴(import)
▶ 전역 네임스페이스에 있는 이름을 참조할 때는 네임스페이스 이름없이 :: 연산자로 시작함
Fully Qualified Name은 네임스페이스의 완전한 이름으로 아래의 예에서 볼 때 MyProject::Connection::NS_CONST, MyProject::Connection::my_function을 의미합니다.
[code php;gutter:true] <?php namespace MyProject::Connection; const NS_CONST = 'namespace constant NS_CONST'; function my_function() { print "namespace function my_function()\n"; } print MyProject::Connection::NS_CONST . "\n"; MyProject::Connection::my_function(); ?> [/code]
다른 네임스페이스에 있는 이름을 참조할 때는 완전한 네임스페이스 이름을 가진 Fully Qualified Name을 사용하여야 하나 현재 네임스페이스에 있는 이름을 참조할 때는 네임스페이스 이름을 생략한 Unqualified Name을 사용할 수 있습니다. 위의 예에서 10, 11번행의 현재 네임스페이스가 MyProject::Connection이므로 아래와 같이 Fully Qualified Name 대신에 Unqualified Name을 사용할 수 있습니다.
[code php;gutter:false] print NS_CONST . "\n"; my_function(); [/code]
전역 네임스페이스(global namespace)는 그 이름을 가지고 있지 않으므로 네임스페이스 이름을 지정할 수 없습니다. 이런 이유로 전역 네임스페이스에 소속된 이름을 참조할 때는 네임스페이스 이름없이 :: 연산자로 시작하는 이름을 사용합니다.
[code php;gutter:false] <?php namespace A::B::C; /* This function is A::B::C::fopen */ function fopen() { /* ... */ $f = ::fopen(...); // call global fopen return $f; } ?> [/code]
use 연산자를 이용하면 스크립트 실행시 다른 네임스페이스를 현재 네임스페이스로 임포트할 수 있습니다. 따라서 소스를 작성하거나 보기에 불편한 긴 Fully Qualified Name 대신에 짧은 별명(alias)을 사용할 수 있습니다. PHP에서 벤치마킹한 것으로 보이는 C++에서는 짧은 이름을 사용하기 위한 방법으로 별명을 이용하는 것 말고도 using 지시자(directive)와 using 선언자(declaration)를 이용하는 방법이 있습니다만 PHP에서는 이러한 방법까지는 제공하지 않습니다.
[code php;gutter:false] <?php namespace MyProject::Connection; function my_class() { /* code */ } ?> [/code]
< inc.php >
[code php;gutter:false] <?php require_once('inc.php'); use MyProject::Connection as Connection; $my = new Connection::my_class(); ?> [/code]
Unqualified 함수 호출
네임스페이스 내에서 Unqualified 함수를 호출하면 아래와 같은 규칙에 따라 해당 함수를 호출합니다.
① 현재 네임스페이스에 정의된 함수를 호출
② PHP 내장(internal; built-in) 함수를 호출
[code php;gutter:true] <?php namespace A::B; $a = ucfirst("testing 'name resolution rules'"); echo($a); function ucfirst($str) { return '<span style="font-size:32pt;color:steelblue">' . ::ucfirst(substr($str, 0, 1)) . '</span>' . substr($str, 1); } ?> [/code]
위 소스의 4번행을 실행하면 7번행에 정의된 현재 네임스페이스의 ucfirst 함수를 호출합니다. 따라서 위 소스를 실행하면 아래와 같은 결과를 얻을 수 있습니다.
Testing 'name resolution rules'
만약 7번행부터 12번행에서와 같이 현재 네임스페이스에 정의된 ucfirst() 함수가 없었다면 PHP 내장함수인 ucfirst()를 바로 호출하였을 것이고 그 결과는 아래와 같았을 것입니다.
Testing 'name resolution rules'
Unqualified 클래스 호출
현재 네임스페이스가 A::B::C일 때, new C()는 A::B::C()로 변환됩니다. 현재 네임스페이스가 A::B일 때, new C()의 호출은 아래와 같은 규칙에 따릅니다.
① 네임스페이스 A::B 내에 정의된 클래스 C에 대한 객체 생성
② PHP 내장(internal; built-in) 클래스에 대한 객체 생성
Qualified 함수 호출
A::B::foo() 함수의 호출은 아래와 같은 규칙에 따릅니다.
① 네임스페이스 A::B 내에 정의된 함수 foo()를 호출
② 클래스 A::B의 정적 멤버 함수 foo()를 호출
[code php;gutter:false] <?php namespace A; // static methods/namespace functions of current namespace A::foo(); // first tries to call function "foo" from namespace "A::A" // then tries to call method "foo" of class "A" from namespace "A" // then tries to call function "foo" from namespace "A" // then calls method "foo" of internal class "A" ?> [/code]
< 출처:php.net >
Qualified 클래스 호출
new A::B::C()는 네임스페이스 A::B의 클래스 C를 참조합니다.
 

Posted by 방글24
phpclass/객체정보2008. 8. 9. 12:36
작성: 2008.08.09
네임스페이스 다루기
임의의 네임스페이스 내에 있는 모든 심볼(클래스명, 함수명, 변수명, 상수명 등)은 네임스페이스_식별자::심볼과 같은 식으로 다루어집니다.
namespaced_idenfitier::symbol;
[code php;gutter:false] <?php namespace MyProject::Connection; function my_class() { /* code */ } ?> [/code]
< inc.php >
[code php;gutter:false] <?php require_once('inc.php'); $my = new MyProject::Connection::my_class(); ?> [/code]
use 연산자(use operator)
use namespaced_name as othername;
use 연산자를 사용하면 원래 네임스페이스 이름 대신에 별명으로 임포트(import)할 수 있기 때문에 긴 이름 대신에 더 짧은 이름을 사용할 수 있어 문서를 단순화 시킬 수 있습니다. 짧은 이름의 othername은 스크립트 문서가 실행될 때 원래의 이름인 namespaced_name으로 변환됩니다.
위의 예제에서 보았듯이 use 연산자를 사용하지 않을 경우에는 MyProject::Connection::my_class()와 같은 전체 이름으로 참조해야 합니다. 그러나 use 연산자를 사용할 경우에는 아래와 같이 Connection::my_class()와 같이 더 짧은 이름을 사용할 수 있습니다.
[code php;gutter:false] <?php require_once('inc.php'); use MyProject::Connection as Connection; $my = new Connection::my_class(); ?> [/code]
또한 use MyProject::Connection as Connection; 에서 Connection과 같이 동일한 이름의 별명을 사용할 경우에는 아래와 같이 as 이하의 구문을 생략할 수 있습니다.
[code php;gutter:false] <?php require_once('inc.php'); use MyProject::Connection; $my = new Connection::my_class(); ?> [/code]
use 연산자는 전역 범위(global scopr)에서만 사용할 수 있으며 함수 또는 클래스 내에서는 사용할 수 없습니다. use 연산자에 의해 임포트된 이름은 임포트된 부분부터 현재 파일의 끝까지 영향을 미칩니다.
상수 __NAMESPACE__(constants __NAMESPACE__)
컴파일-타임 상수인 __NAMESPACE__ 에는 현재 네임스페이스 이름을 가지고 있습니다. 네임스페이스를 벗어나면 빈문자열(empty string)을 값으로 할당됩니다. 따라서 전역 네임스페이스(global namespace)에서 __NAMESPACE__의 값은 빈문자열입니다.
[code php;gutter:false] <?php namespace A::B::C; function foo() { // do stuff } set_error_handler(__NAMESPACE__ . "::foo"); ?> [/code]
< 출처:php.net >

Posted by 방글24
phpclass/객체정보2008. 8. 9. 12:35
작성: 2008.08.09
내장 네임스페이스
php v5.3 이전의 네임스페이스
php v5.3 이전에도 내장된 네임스페이스가 존재하고 있었습니다.
[code php;gutter:false] <?php $str = "전역 네임스페이스(global namespace)의 변수"; function test() { $str = "지역 네임스페이스(local namespace)의 변수"; print "$str<BR>\n"; global $str; print "$str<BR>\n"; } test(); ?> [/code]
test() 함수 내에서 동일한 변수이름 $str이 두 번 사용되고 있지만 하나는 지역 네임스페이스에서, 다른 하나는 전역 네임스페이스의 이름을 참조합니다. 따라서 이 소스를 실행하면 아래와 같이 나타날 것입니다.
전역 네임스페이스 - 함수(또는 클래스) 바깥쪽에서 정의된 이름
지역 네임스페이스 - 함수(또는 메소드) 안에서 정의된 이름 또는 클래스 안에서 정의된 이름
이와 같이 php v5.3 이전에도 내장된 네임스페이스가 존재하고 있어서 묵시적(implicit)으로 사용하여 왔으나, php v5.3 부터는 프로그래머가 새로운 네임스페이스를 직접 명시적(explicit)으로 정의하여 사용할 수 있도록 하였습니다.
전역 네임스페이스(Global namespace; Global space)
네임스페이스를 지원하는 php v5.3에서 네임스페이스가 정의되지않은 클래스 및 함수는 전역 네임스페이스에 존재합니다. 전역 네임스페이스는 특정한 이름(identifier)을 가지고 있지 않기 때문에 네임스페이스_식별자::함수명과 같은 식으로 호출할 수가 없으므로 이 때는 네임스페이스_식별자없이 범위지정자 ::로 시작되는, 즉 ::함수명과 같은 식으로 전역 네임스페이스에 존재하는 함수를 호출합니다.
[code php;gutter:false] <?php namespace A::B::C; /* This function is A::B::C::fopen */ function fopen() { /* ... */ $f = ::fopen(...); // call global fopen return $f; } ?> [/code]
< 출처:php.net >
 

Posted by 방글24
phpclass/객체정보2008. 8. 9. 12:34
작성: 2008.08.09
네임스페이스의 정의
이 문서에서 기술된 모든 소스는 2008년 8월 6일자 php-5.3.0 알파2 개발버전(php5.3-win32-200808062030.zip)으로 테스트하였습니다.
네임스페이스의 정의는 namespace 키워드에 이어 네임스페이스 식별자(identifier)를 지정합니다.
namespace identifier;
스크립트 문서 단위로 이루어지는 네임스페이스
C++과 같은 다른 언어와 달리 php v5.3에서 제공하는 이름공간 기능은 스크립트 문서 단위로 이루어진다는 것입니다. 그리고 네임스페이스의 지정은 다른 실행코드에 앞서 정의되어야 합니다. 그렇지 않고 네임스페이스를 정의하기 전에 다른 실행코드가 문서에 나타나면 아래와 같은 에러를 발생시킵니다.
Fatal error: Namespace declaration statement has to be the very first statement in the script in test.php on line xx
A_SPACE와 B_SPACE 네임스페이스 각각에 존재하는 동일한 이름의 함수 my_function()가 어떻게 동작하는지에 대한 간단한 예를 살펴보겠습니다.
[code php;gutter:false] <?php namespace A_SPACE; function my_function() { print "A_SPACE에서 정의한 함수\n"; } ?> [/code]
< inc1.php >
[code php;gutter:false] <?php namespace B_SPACE; function my_function() { print "B_SPACE에서 정의한 함수\n"; } ?> [/code]
< inc2.php >
[code php;gutter:false] <?php require_once('inc1.php'); require_once('inc2.php'); A_SPACE::my_function(); B_SPACE::my_function(); ?> [/code]
< test.php >
test.php를 실행하게 되면 아래와 같은 결과를 얻을 수 있습니다.
A_SPACE에서 정의한 함수
B_SPACE에서 정의한 함수
여러 개의 파일이 동일한 네임스페이스를 사용하는 예
동일한 명칭의 네임스페이스를 여러 파일에서 사용할 수 있기 때문에 동일한 네임스페이스 내에서 여러 파일로 나누어 작업할 수 있습니다.
[code php;gutter:false] <?php namespace MyProject::Connection; const NS_CONST = 'namespace constant NS_CONST'; class my_class { const MY_CONST = 'my_class::MY_CONST'; public static $public_var = "my_class::public_var"; public function my_function() { print "my_class::my_function()\n"; } } function my_function() { print "namespace function my_function()\n"; } ?> [/code]
< inc1.php >
[code php;gutter:false] <?php namespace MyProject::Connection; class my_class2 extends my_class { public function my_function() { my_class::my_function(); print "my_class2::my_function()\n"; } } ?> [/code]
< inc2.php >
[code php;gutter:false] <?php require_once('inc1.php'); require_once('inc2.php'); print MyProject::Connection::NS_CONST . "\n"; MyProject::Connection::my_function(); print MyProject::Connection::my_class2::MY_CONST . "\n"; print MyProject::Connection::my_class2::$public_var . "\n"; MyProject::Connection::my_class2::my_function(); ?> [/code]
< test.php >
test.php를 실행하게 되면 아래와 같은 결과를 얻을 수 있습니다.
[code php;gutter:false] namespace constant NS_CONST namespace function my_function() my_class::MY_CONST my_class::public_var my_class::my_function() my_class2::my_function() [/code]

Posted by 방글24
phpclass/객체정보2008. 8. 9. 12:32
작성: 2008.08.09
네임스페이스의 필요성
PHP로 프로그래밍할 때 보통 일인체제하에 소규모로 만들어지는 경우가 많다 보니 함수명, 클래스명과 같은 이름이 중복되어 충돌되는 것을 쉽게(?) 피할 수 있었습니다. 그러나 이제 PHP도 여러 사람이 공동 작업하는 경우도 많게 되고 제작되는 프로그램 덩치도 커지고 복잡해지다보니 이름의 중복으로 발생하는 충돌을 피하기가 쉽지 않게 되었습니다.
이러한 이유로 PHP의 미래 버전인 v6에서는 처음부터 네임스페이스를 지원하도록 설계/제작되고 있습니다. 이러한 와중에 그동안의 알려진 바와 같이 php v5.3에 네임스페이스 기능이 이식되어 2008년 8월 1일에 php 5.3 알파1 버전으로 정식으로 공개되었습니다.
객체지향언어 중에는 네임스페이스를 지원하지 않는 언어도 있는 이유로 혹자는 네임스페이스가 꼭 필요한 것이냐고 반문하는 분도 계시지만 네임스페이스가 객체화 도구가 아니고 모듈화 도구이기 때문에 객체지향언어와 무관하게 좀 더 개선된 모듈화 프로그래밍을 할 수 있는 좋은 도구라고 이해하면 될 것 같습니다.
사실 클래스를 이용하면 네임스페이스 지원없이도 함수명, 변수명, 상수명 등의 중복으로 발생하는 충돌을 잘 피할 수 있습니다. 그러나 클래스명의 경우는 그렇지 않습니다. PHP v5 버전이 공개되면서 새로 개발되는 거의 대부분의 PHP 코드들이 클래스를 이용하게 된 현실로 볼 때 모듈화 도구로서 자연스럽게 네임스페이스을 지원하게 된 것 같습니다.
"클래스를 사용해야하나?"라는 문서를 통해 모듈화에서 네임스페이스(namespace; 이름공간)의 역할에 대하여 설명드렸던 것처럼 갈수록 점점 복잡해지고 덩치가 커져가는 PHP 라이브러리와 같은 프로그램에서 함수명, 클래스명, 변수명 등의 이름 충돌 문제를 자주 접하게 됩니다. 그동안 대부분의 프로그래머들은 이러한 문제를 피하기 위하여 이름 앞에 해당 프로그램의 특징을 나타내는 접두어를 붙이는 방법을 주로 사용하여 왔으나 이러한 방법은 이름의 길이를 길게 만들기 때문에 소스를 복잡하게 만드는 등 좋지않은 요인으로 작용하였습니다.
예를 들어 후키템플릿 라이브러리에서 정의된 클래스명을 보면 아래와 같이 대부분의 클래스명에는 접두어로 hTemplate가 붙습니다. 이것은 다른 라이브러이 등과 함께 사용할 때 이름이 충돌되는 것을 피하기 위한 한 방편으로 사용된 것입니다.
[code php;gutter:false] <?php class hTemplateCache { /* code */ } class hTemplateCompile { /* code */ } class hTemplateDebug { /* code */ } class hTemplateFile { /* code */ } class hTemplateFilter { /* code */ } class hTemplateFunction { /* code */ } class hTemplateStructure { /* code */ } class hTemplateAssign { /* code */ } class hTemplate extends hTemplateAssign { /* code */ } $tpl = new hTemplate(); ?> [/code]
만약 PHP 5.3.0 버전부터 제공되는 namespace 키워드를 이용한다면 아래와 같이 클래스명에 접두어 hTemplate를 붙이지 않더라도 다른 프로그램과 이름이 충돌하는 것을 피할 수 있을 것입니다.
[code php;gutter:false] <?php namespace hTemplate; class Cache { /* code */ } class Compile { /* code */ } class Debug { /* code */ } class File { /* code */ } class Filter { /* code */ } class Function { /* code */ } class Structure { /* code */ } class Assign { /* code */ } class main extends Assign { /* code */ } $tpl = new hTemplate::main(); ?> [/code]
또 다른 예로 PEAR의 HTTTP 패키지를 살펴보면 아래와 같은 "HTTP", "HTTP_Downlod", "HTTP_Request"와 같은 특정 접두어를 붙인 클래스명을 사용하여 클래스명이 다름 프로그램과 충돌될 가능성을 사전에 피하려고 한 것을 볼 수 있습니다.
HTTP_Download
HTTP_Client
HTTP_Header
HTTP_Request
HTTP_Download_PgLOB
HTTP_Download_Archive
HTTP_Header_Cache
HTTP_Request_Listener
< PEAR HTTP 패키지에서 사용된 클래스명 >
PEAR2 Coding Standards(pear.php.net/manual/en/pear2cs.php)의 Rule을 보면 PEAR2의 모든 클래스와 함수는 아래와 같이 반드시 PEAR2의 네임스페이스를 정의하도록 하고 있습니다. (아래의 세번째 예는 설명을 위해 추가하였음)
[code php;gutter:false] <?php namespace PEAR2; class MyClass { /* code */ } ?> [/code]
[code php;gutter:false] <? namespace PEAR2::HTTP; class Request { /* code */ } ?> [/code]
[code php;gutter:false] <? namespace PEAR2::HTTP::Request; class Listener { /* code */ } ?> [/code]
이와 같이 네임스페이스를 이용하면 클래스명에 더 이상 접두어를 붙일 필요가 없습니다.
Download
Client
Header
Request
PgLOB
Archive
Cache
Listener
< 네임스페이스 지원으로 간략화된 클래스명 >
이와 같이 네임스페이스의 지원으로 간단명료한 클래스명을 사용하여 프로그래밍할 수 있게 되었으며 단지 각 클래스들이 어느 네임스페이스에 속해 있는지만 명확히 지정해주면 됩니다.

Posted by 방글24
phpclass/객체정보2008. 8. 9. 12:28
작성: 2008.08.09
네임스페이스란?
많은 사람들이 네임스페이스(namespace; 이름공간) 개념을 이해하는데 어려움을 겪는 것 같습니다. 2008년 8월 1일 php.net에 정식 릴리즈된 5.3.0 알파1 버전부터 네임스페이스를 지원한다기에 네임스페이스를 이곳에서 다시 한 번 정리하게 되었습니다.
프로그램 소스의 여러 가지 구성요소 중에는 클래스명, 함수명, 변수명, 상수명과 같이 이름(identifier)들이 포함되어 있는데 이러한 이름들이 동일한 이름으로 중복하여 사용하더라도 서로 충돌되지 않도록 해주는 기능이 네임스페이스입니다.
초등학교 교실
예를 들어, 어떤 초등학교 6학년 1반에 홍길동이란 이름을 가진 어린이가 3명 있다고 하면 이 반 담임선생님은 출석을 부를 때마다 곤혹을 치를 것입니다.
키큰 홍길동~
키작은 홍길동~
뚱뚱한 홍길동~
< 6학년 1반에 편성된 3명의 홍길동 어린이 >
그렇다고 아이들보고 이름 바꾸라고 할 수 있습니까? 불편하지만 홍길동 앞에 "키큰", "키작은", "뚱뚱한"과 같은 접두어를 붙일 수 밖에 없겠지요.
사실 이 문제를 근본적으로 해결하려고 했다면 학년초 반편성할 때 3명의 홍길동 어린이를 서로 다른 반으로 편성했을 것입니다.
< 각 반으로 편성되어 관리되고 있는 3명의 홍길동 >
이제는 "키큰", "키작은", "뚱뚱한"이라는 괴상망칙한 수식어를 붙이지 않아도 됩니다. 각 반에서는 모두 수식어 없이 그냥 홍길동이라고 부르면 되고, 같이 있을 때는 1반 홍길동, 2반 홍길동, 3반 홍길동 라고 부르면 되지요.
네임스페이스(namespace; 이름공간)
네임스페이스란 그 이름이 뜻하는 바와 같이 서로 관련된 이름(함수명, 클래스명 ...)을 통합관리하기 위하여 지정하는 공간을 의미합니다. 초등학교 각 반 교실에 해당합니다. 키큰 홍길동은 1반에, 키작은 홍길동은 2반에, 뚱뚱한 홍길동은 3반에 소속시키는 것입니다. 이를 개념적으로 프로그래밍해보면 아래와 같습니다.
[code php;gutter:false] <?php namespace 1반; function 홍길동() { print "나는 키큰 홍길동이다.\n"; } ?> [/code]
[code php;gutter:false] <?php namespace 2반; function 홍길동() { print "나는 키작은 홍길동이다.\n"; } ?> [/code]
[code php;gutter:false] <?php namespace 3반; function 홍길동() { print "나는 뚱뚱한 홍길동이다.\n"; } ?> [/code]
홍길동을 부를 때 1반 홍길동, 2반 홍길동, 3반 홍길동 라고 부르듯이 프로그램 소스에서는 아래와 같이 1반::홍길동(), 2반::홍길동(), 3반::홍길동() 라는 식으로 호출하면 됩니다.
[code php;gutter:false] <?php 1반::홍길동(); 2반::홍길동(); 3반::홍길동(); ?> [/code]
각 반에서 반에 소속된 어린이의 이름을 출석부에 기록하여 관리하는 것처럼 각 네임스페이스에 소속된 이름을 심볼 테이블(symbol table)에 기록하여 관리합니다.

'phpclass > 객체정보' 카테고리의 다른 글

{네임스페이스}3.네임스페이스의 정의  (0) 2008.08.09
{네임스페이스}2.네임스페이스의 필요성  (0) 2008.08.09
What Is an Interface?  (0) 2005.10.25
{참조}5.참조 삭제  (0) 2001.03.02
{참조}4.참조 반환  (0) 2001.03.02
Posted by 방글24
phpclass/객체정보2005. 10. 25. 22:57

What Is an Interface?

(출처)

   http://java.sun.com/docs/books/tutorial/java/concepts/interface.html

현재 이 웹사이트 주소에 접속할 수 없어서 해당자료를 임시로 이곳에 올립니다.

현재 이 웹사이트의 자료가 새롭게 업데이트되었습니다.

수정되기 전의 문서(2005년 10월 이전 자료)를 임시로 이곳에 올립니다.


 

 

The JavaTM Tutorial

 

 


Trail: Learning the Java Language
Lesson: Object-Oriented Programming Concepts

 

What Is an Interface?

In general, an interface is a device or a system that unrelated entities use to interact. According to this definition, a remote control is an interface between you and a television set, the English language is an interface between two people, and the protocol of behavior enforced in the military is the interface between people of different ranks.

 

Within the Java programming language, an interface is a type, just as a class is a type. Like a class, an interface defines methods. Unlike a class, an interface never implements methods; instead, classes that implement the interface implement the methods defined by the interface. A class can implement multiple interfaces.

 

The bicycle class and its class hierarchy define what a bicycle can and cannot do in terms of its "bicycleness." But bicycles interact with the world on other terms. For example, a bicycle in a store could be managed by an inventory program. An inventory program doesn’t care what class of items it manages, as long as each item provides certain information, such as price and tracking number. Instead of forcing class relationships on otherwise unrelated items, the inventory program sets up a protocol of communication. This protocol comes in the form of a set of method definitions contained within an interface. The inventory interface would define, but not implement, methods that set and get the retail price, assign a tracking number, and so on.

 

To work in the inventory program, the bicycle class must agree to this protocol by implementing the interface. When a class implements an interface, the class agrees to implement all the methods defined in the interface. Thus, the bicycle class would provide the implementations for the methods that set and get retail price, assign a tracking number, and so on.

 

You use an interface to define a protocol of behavior that can be implemented by any class anywhere in the class hierarchy. Interfaces are useful for the following:

 

  • Capturing similarities among unrelated classes without artificially forcing a class relationship
  • Declaring methods that one or more classes are expected to implement
  • Revealing an object's programming interface without revealing its class
  • Modelling multiple inheritance, a feature that some object-oriented languages support that allows a class to have more than one superclass

 

 

 

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.


Posted by 방글24
phpclass/객체모델2005. 10. 11. 12:27
작성: 2008.09.05
final 키워드
final 키워드는 해당 메소드와 클래스를 변경할 수 없게 보호해 주는 역할을 합니다. final로 정의된 메소드는 재정의(overriding;오버라이딩)할 수 없으며, final로 정의된 클래스는 상속이 불가능하여 자식 클래스를 만들 수 없습니다. 이와 같이 final 키워드를 이용하면 메소드를 재정의하거나 클래스를 상속할 수 없기 때문에 메소드 또는 클래스의 내용을 적절히 보호할 수 있습니다.
클래스 내의 일부 메소드만 그 내용을 숨기려면 해당 메소드명 앞에 final 키워드를 붙이면 되고, 클래스 전체 내용을 숨기려면 클래스명 앞에 final 키워드를 붙이면 됩니다. 클래스에 final 키워드를 붙이면 해당 클래스 내의 각각의 메소드에는 final 키워드를 붙일 필요가 없이 모두 생략할 수 있습니다.
[메소드에 final 키워드 사용] 자식 클래스에서 재정의(overriding;오버라이딩)를 할 수 없음
[클래스에 final 키워드 사용] 상속이 불가능하여 자식 클래스를 만들 수 없음
자바의 경우에는 멤버 변수(필드)에도 final을 붙일 수 있는데 이는 PHP에서 멤버상수를 만들기 위하여 const를 붙이는 것과 같은 역할을 합니다.
C++의 경우에는 final 키워드의 필요성이 그리 크지 않다고 생각하여 final 키워드를 언어차원에서 지원하지 않는다고 합니다. 각 언어에 적용되는 키워드를 비교하면 아래와 같습니다.
< 각 언어별 적용 키워드 >
적용대상 C++ Java PHP
멤버변수 const final const
메소드 - final final
클래스 - final final
Final 메소드
Final 메소드는 클래스를 상속할 때 재정의(overriding;오버라이딩)할 수 없도록 구현된 메소드입니다.
[code php;gutter:true] <?php class BaseClass { final public function test() { echo "BaseClass::test() called\n"; } } class ChildClass extends BaseClass { public function test() { echo "ChildClass::test() clased\n"; } } // Fatal error: Cannot override final method BaseClass::test() ?> [/code]
< 재정의할 수 없는 Final 메소드 >
부모 클래스에서 정의된 Final 메소드는 그 구현이 변경되지 않도록 보호하기 위한 것이므로 만약 위의 예의 9행과 같이 Final 메소드를 재정의(overriding;오버라이딩)하려고 시도하게 되면 14행과 같은 에러가 발생합니다.
[code php;gutter:false] <?php class BaseClass { public function test() { echo "BaseClass::test() called\n"; } } class ChildClass extends BaseClass { final public function test() { echo "ChildClass::test() called\n"; } } // Call the override function in child class ChildClass::test(); ?> [/code]
< 자식클래스에서 정의한 Final 메소드 >
final & private
final과 private는 서로 다른 역할을 수행하는 modifier이지만 Java Language Specification-Second Edition(http://java.sun.com/docs/books/jls/second_edition/html /jTOC.doc.html)의 8.4.3.3 내용을 살펴보면 알 수 있듯이 자식 클래스에서 상속받을 수 없다는 면에서는 비슷합니다.
8.4.3.3 final Methods

A method can be declared final to prevent subclasses from overriding or hiding it. It is a compile-time error to attempt to override or hide a final method.

A private method and all methods declared in a final class (§8.1.1.2) are implicitly final, because it is impossible to override them. It is permitted but not required for the declarations of such methods to redundantly include the final keyword.

It is a compile-time error for a final method to be declared abstract.
< final & private modifier >
그러나 상속할 수 없다는 면에서는 비슷하지만 동일하지는 않습니다. final 메소드를 자식 클래스에서 재정의하면 위에서 살펴보았듯이 재정의할 수 없다는 Fatal 에러를 발생시키지만 private 메소드의 경우는 아무런 에러도 발생시키지 않습니다. 잘 동작합니다. 그렇다고 private 메소드가 자식 클래스에서 상속받아 재정의 된 것은 아닙니다. private 메소드는 상속되지 않습니다. 상속되지 않는다는 것과 상속할 수 없다는 것은 전혀 다른 의미입니다.
6.6.8 Example: private Fields, Methods, and Constructors

A private class member or constructor is accessible only within the class body in which the member is declared and is not inherited by subclasses.
< private modifier >
자식 클래스에서 부모 클래스의 private로 정의된 메소드와 동일한 이름의 메소드를 정의하더라도 이 메소드는 부모 클래스의 private 메소드와는 전혀 상관없는 새로운 메소드입니다. 왜냐하면 부모 클래스의 private 메소드는 자식 클래스로 상속되지 않기 때문에 자식 클래스에서 볼 때는 부모 클래스의 private 메소드는 없는 것과 같습니다.
[code php;gutter:true] <?php class BaseClass { private function test() { echo "BaseClass::test() called\n"; } } class ChildClass extends BaseClass { public function test() { echo "ChildClass::test() called\n"; } } // Output ChildClass::test() called ChildClass::test(); ?> [/code]
9행에서 부모 클래스에서 private로 정의된 메소드와 같은 이름의 메소드를 정의하였지만 전혀 에러가 발생하지 않았으며 15행에서 보듯이 자식 클래스에서 새롭게 정의된 메소드를 잘 실행하고 있습니다.
Final 클래스
Final 클래스는 더 이상 상속할 수 없는, 즉 자식 클래스를 만들 수 없는 클래스입니다. Final 클래스의 모든 메소드는 저절로 final 메소드가 됩니다.
[code php;gutter:true] <?php final class BaseClass { public function test() { echo "BaseClass::test() called\n"; } // Here it doesn't matter if you specify the function as final or not final public function moreTesting() { echo "BaseClass::moreTesting() called\n"; } } class ChildClass extends BaseClass { } // Fatal error: Class ChildClass may not inherit from final class (BaseClass) ?> [/code]
< Final 클래스의 예(출처: php.net) >
13행과 같이 Final 클래스를 상속하려고 시도하면 14행과 같은 에러가 발생합니다.
Final 필드
자바에만 있는 개념으로 필드는 클래스 내부에 존재하는 변수를 의미합니다. Final 필드인 경우에는 필드 내용을 변경할 수 없습니다. 이와같이 자바에서는 final 키워드를 사용하여 상수를 만들 수 있습니다.
[code java;gutter:false] final float pi = 3.141592; final boolean status = true; final int tall = 176; final int[] numbers = {1, 2, 3, 4, 5}; [/code]
< 자바에서의 Final 필드 >
이러한 자바의 Final 필드는 PHP에서 상수와 같은 개념이지요.
[code php;gutter:true] <?php class MyClass { const pi = 3.141592; const status = true; const tall = 176; // Fatal error: Arrays are not allowed in class constants const numbers = array(1, 2, 3, 4, 5); } echo MyClass::pi . "\n"; echo MyClass::status . "\n"; echo MyClass::tall . "\n"; echo MyClass::numbers . "\n"; ?> [/code]
< PHP에서의 상수 정의와 사용법 >
자바와는 달리 PHP에서는 7행, 13행에서와 같이 배열 상수를 정의하여 사용할 수 없으므로 에러가 발생합니다. 참고로 자바에서의 배열 상수는 다른 형식의 상수와 좀 다른 의미를 가지고 있습니다. 배열명(변수형식, 차원, 배열크기 ...) 자체가 상수라는 의미이며 각 배열 항목의 값은 언제든지 변경할 수 있습니다.

Posted by 방글24
phpclass/객체모델2005. 10. 11. 12:24
written: Oct 20 2005
인터페이스의 개념
인터페이스(interface)의 정확한 의미를 알기 위해서는 자바를 개발한 선의 자바 투토리얼(sun's The Java Tutorial) 중에서 What Is an Interface?를 참고하는 것을 좋을 듯합니다.
이 문서에 의하면 일반적인 의미에서의 인터페이스란 "상관관계없는 물체들간의 상호작용을 위해 사용되는 장치"라고 합니다.
프로그래머들이 생활속에서 쉽게 접할 수 있는 인터페이스라면 컴퓨터 소프트웨어를 들 수 있을 것입니다. 컴퓨터 하드웨어라는 것은 인간의 감각과는 거리가 먼 이물질이기 때문에 중간에 소프트웨어라는 인터페이스를 통해 인간의 시각, 촉각과 청각 등으로 컴퓨터 하드웨어를 손쉽게 다룰 수 있습니다.
< 인터페이스의 개념 >
이와같이 서로 다른 특성을 가진 물체를 서로 연결해 주는 매개체 역활을 하는 것이 인터페이스인 것처럼 객체 인터페이스(Object Interfaces)도 관련성없는 객체들간에 상호작용할 수 있도록 매개역할을 하는 객체라고 할 수 있습니다.
클래스의 상속 개념과 비교할 때
상위클래스로부터 상속되는 하위클래스는 상위클래스와 상당히 밀접한 관계를 가지고 있습니다. 서로 관련없는 클래스를 상속받을 이유가 없는 것이지요.
그러나 인터페이스는 클래스와의 관계성이 중요하지 않으며 클래스는 인터페이스의 멤버를 상속 구조에 관계없이 어디에서나 자유롭게 접근할 수 있어야 합니다.
인터페이스의 개요
추상클래스는 추상메쏘드 뿐만 아니라 추상메쏘드가 아닌 멤버들도 제한없이 포함할 수 있습니다. 반면에 인터페이스의 모든 메쏘드는 반드시 추상메쏘드이어야 하며 그 구현은 인터페이스를 구현하는 클래스에서 처리하여야 합니다.
추상메쏘드 이외에 인터페이스에 포함될 수 있는 멤버로는 상수가 있습니다.
인터페이스의 구문형식
class 키워드 대신에 interface 키워드를 사용한다는 것을 빼면 인터페이스의 구문형식은 class 구문형식과 동일합니다.
'인터페이스의 개념'에서도 살펴보았듯이 클래스는 상속 구조와 관계없이 인터페이스의 모든 멤버를 자유롭게 접근할 수 있어야 하기 때문에 인터페이스에 선언되는 멤버(메쏘드, 상수)는 반드시 public visibility를 가지고 있어야 합니다.
interface 인터페이스이름 {
  /**
  * 상수 선언
  */
  const 상수이름 = 상수값;

  /**
  * 메쏘드 선언
  */
  [abstract] [public] function 메쏘드이름(인수리스트);
}
클래스 상수는 그 자체로 항상 public visibility 특성을 가지고 있기 때문에 const 키워드 뒤에 상수이름과 그 값을 나타내주면 됩니다.
추상클래스에서는 추상메쏘드와 추상메쏘드가 아닌 메쏘드를 구별하기 위하여 추상메쏘드를 선언할 때 반드시 abstract 키워드를 지정해 주어야 합니다. 반면에 인터페이스에는 추상메쏘드만 존재하기 때문에 abstract 키워드를 생략하더라도 추상메쏘드로 자동인식하므로 별문제 없이 정상적으로 실행됩니다.
public 키워드도 마찬가지로 인터페이스에 선언된 메쏘드는 무조건 public visibility이기 때문에 생략하더라도 자동적으로 public으로 인식합니다.
이와같이 인터페이스에 선언된 메쏘드는 abstract 또는 public 키워드를 지정하지 않더라도 그 메쏘드는 기본적으로 public하고 abstract한 메쏘드로 인식하게 됩니다.
인터페이스의 구현
클래스에서 인터페이스를 구현하려면 implements 키워드를 클래스 정의에 포함시켜야 합니다. 클래스를 상속하는 것과는 달리 인터페이스는 여러개의 인터페이스를 동시에 구현할 수 있으며, 이 때 각 인터페이스는 콤마(,)로 구분하여 표기합니다.
class 클래스이름 implements 인터페이스이름[, 인터페이스이름[, ......]] {
  /**
  * 인터페이스에 선언된 모든 메쏘드를 구현
  */
  ......
}
인터페이스를 사용하여 작성된 클래스는 인터페이스에 선언된 모든 메쏘드의 몸체를 구현하여야 합니다.    
[code php;gutter:false] <?php /** * declare the interface 'my_interface' */ interface my_interface { public function set_variable($name, $value); public function get_variable($name); } /** * implement the interface */ class my_class implements my_interface { private $vars = array(); public function set_variable($name, $value) { $this->vars[$name] = $value; } public function get_variable($name) { return $this->vars[$name]; } } ?> [/code]
< 인터페이스의 구현 예제 >
추상클래스를 상속받는 하위클래스는 추상클래스에 선언된 추상메쏘드의 구현을 선택적으로 할 수 있었습니다만 인터페이스를 사용하는 클래스는 인터페이스에 선언된 모든 추상메쏘드를 구현해주어야 합니다. 따라서 아래와 같이 인터페이스에 선언된 메쏘드 중에 하나라도 빠지게 되면 치명적인 에러(fatal error)가 발생합니다.
[code php;gutter:false] <?php interface my_interface { public function set_variable($name, $value); public function get_variable($name); } /** * This will not work * Fatal error Class my_class contains 1 abstract methods * and must therefore be declared abstract (my_interface::get_variable) */ class my_class implements my_interface { private $vars = array(); public function set_variable($name, $value) { $this->vars[$name] = $value; } } ?> [/code]
< 잘못 구현된 문장 >
상속 중인 클래스에서의 인터페이스 구현
상속 중인 하위클래스에서도 아래와 같은 형식으로 인터페이스를 구현할 수 있습니다.
class 클래스이름 extends 상위클래스이름 implements 인터페이스이름[, 인터페이스이름[, ......]] {
  /**
  * 인터페이스에 선언된 모든 메쏘드를 구현
  */
  ......
}
다중 인터페이스
객체지향 프로그래밍을 하다보면 단일 상속으로는 해결하기 어렵고 두 개 이상의 클래스로부터 멤버를 상속해야 할 경우가 많이 발생합니다. 이러한 경우에 C++과 같은 객체지향언어에서는 다중상속(multiple inheritance)을 통해 해결할 수 있습니다.
다중상속에 대하여는 "상단메뉴 >> 클래식&객체 > Zend2객체모델 > 다중상속"을 참조바랍니다.
위 문서를 보면 알겠지만 다중상속에 의해 클래스의 상속 구조가 매우 복잡해지고, 멤버 특성의 상속 관계가 모호해 질 수 있기 때문에 간단한 구조를 지향하는 자바에서는 다중상속을 허용하지 않고 대신에 다중 인터페이스를 이용하게 됩니다.
PHP5에서도 자바의 영향을 받아 단일상속만을 지원하기 때문에 상속 개념으로는 해결할 수 없으며 자바와 같이 다중 인터페이스를 통해 이러한 문제를 해결하고 있습니다.
[code php;gutter:false] <?php /** * 더하기 계산 인터페이스 */ interface plus { abstract public function calc_plus($arg1, $arg2); } /** * 빼기 계산 인터페이스 */ interface minus { abstract public function calc_minus($arg1, $arg2); } /** * 계산기 클래스 */ class calc implements plus, minus { private $value; public function calc_plus($arg1, $arg2) { $this->value = $arg1 + $arg2; } public function calc_minus($arg1, $arg2) { $this->value = $arg1 - $arg2; } public function result() { return $this->value; } } ?> [/code]
< 다중 인터페이스 예제 >
다중 인터페이스의 메쏘드 이름 충돌
다중 인터페이스로 구현된 클래스를 작성하려면 먼저 각 인터페이스에서 선언된 메쏘드의 이름이 중복되어 선언되지 않았는지 확인하여야 합니다. 만약 동일한 이름의 멤버를 가진 인터페이스들을 다중 인터페이스하게 되면 아래와 같은 에러가 발생합니다.
Fatal error: Can't inherit abstract function minus::same_name() (previously declared abstract in plus) ...
자바의 경우에는 동일한 메쏘드 이름이라도 인자값의 데이터형이나 인자의 갯수가 다르면 실제로는 다른 메쏘드로 인식하기 때문에 문제가 발생하지 않습니다. 이것은 중복정의(overloading) 개념에 해당합니다만 PHP에서는 중복정의(overloading)를 지원하지 않기때문에 인자의 갯수가 다르더라도 동일한 메쏘드로 인식하여 역시 위와 동일한 에러가 발생합니다.
중복정의(overloading)에 대하여는 "상단메뉴 >> 클래식&객체 > 객체지향언어로서의 PHP > 함수 중복정의"를 참조바랍니다.
인터페이스의 상속
인터페이스도 클래스와 같이 상속할 수 있습니다. 클래스를 상속할 때와 마찬가지로 인터페이스를 상속할 때도 extends 키워드를 사용합니다.
interface 인터페이스이름 extends 인터페이스이름[, 인터페이스 이름[, ......]] { ...... }
인터페이스를 상속할 때는 implements 키워드를 사용하지 않고 클래스와 같이 extends 키워드를 사용하는 것에 주의하기 바랍니다.
인터페이스의 다중상속
인터페이스는 클래스와 달리 여러개의 상위 인터페이스(super interface)를 다중상속(multiple inheritance)할 수 있습니다. 각각의 상위 인터페이스는 extends 부분에 콤마(,)로 분리하여 표기합니다.
interface seoul { ...... }
interface pusan { ...... }
interface korea_city extends seoul, pusan { ...... }
인터페이스를 이용한 예제 작성
이제 한가지 예를 통해 인터페이스를 이용하는 방법에 대하여 살펴보겠습니다.
이 예제는 각 도형(삼각형, 사각형, 원형)에 대한 면적을 구하고, 각 도형별로 구한 면적들의 평균값과 최대값을 구하는 프로그램으로 인터페이스를 이용하여 작성해 보겠습니다.
[code php;gutter:false] /** * 도형 면적값을 획득하는 인터페이스 */ interface figure_area { /*abstract public*/ /*double*/ function get_area(); } [/code]
< 인터페이스 figure_area >
figure_area 인터페이스는 단 하나의 추상메쏘드 get_area로 구성되어 있으며, 어떤 도형인지는 알 수 없으나 그 면적값을 획득할 목적으로 작성된 것입니다. 이 인터페이스를 이용하여 삼각형, 사각형 및 원의 면적을 계산하고 계산된 값을 획득하는 각각의 클래스도 만들 것입니다.
figure_area 인터페이스만 보면 어떤 도형의 면적값을 획득하는 것인지를 알 수 없으나 이러한 미지의 도형 면적값을 추상메쏘드 get_area()를 통해 여러개를 입력받아 그 모든 값의 합과 평균값을 구하고 입력된 값 중에 최대값을 구하는 calculate 클래스를 작성해 보겠습니다.  
[code php;gutter:false] /** * 합, 평균값, 최대값을 구하는 클래스 */ class calculate { /*double*/ private $sum = 0; /*figure_area*/ private $maximum; /*integer*/ private $count = 0; public function add(/*figure_area*/ $x) { if ($x instanceof figure_area) { $this->sum += $x->get_area(); if ($this->count == 0 || $this->maximum->get_area() < $x->get_area()) { $this->maximum = $x; } $this->count++; } else { die("The variable x is not instance of figure_area\n"); } } public /*figure_area*/ function get_maximum() { return $this->maximum; } public /*double*/ function get_average() { return $this->sum / $this->count; } } [/code]
< 인터페이스 calculate >
PHP에서는 데이터형을 지정하지 않으나 이 예제에서는 소스의 정확한 의미를 전달하기 위하여 각 멤버 및 각 함수 입출력값의 데이터형을 주석으로 표기하였습니다.
calculate 클래스에 정의된 add 메쏘드의 입력값인 $x의 데이터형(data type)은 figure_area 인터페이스입니다.
10번행에 의해 $x가 figure_area 인터페이스의 멤버인지 확인한 후 멤버가 아니라면 20번행에 의해 에러메시지를 나타낸 후 즉시 스크립트를 종료합니다.
$x가 figure_area 인터페이스의 멤버인 경우에만 합과 최대값을 구하고 카운팅한 후  add 메쏘드 실행를 마칩니다. 13번행부터 16번행까지의 소스를 살펴보면 최대값을 기록하는 멤버 $maximum에는 면적의 실제 최대값이 기록되는 것이 아니라 최대값을 가지고 있는 figure_area형의 객체를 기록하고 있습니다.
24번행에 정의된 get_maximum 메쏘드는 add 메쏘드를 통해 입력된 여러개의 figure_area형 객체 중에서 최대면적값을 가지고 있던 객체를 반환해 줍니다. 따라서 get_maximum 메쏘드의 반환되는 값의 데이터형은 figure_area입니다.
28번행에 정의된 get_average 메쏘드는 입력된 값의 합을 입력된 값의 수로 나눈 평균값을 반환합니다.
마지막으로 figure_area  인터페이스를 구현해줄 각 도형에 대한 클래스를 작성하겠습니다.
[code php;gutter:false] /** * 삼각형 면적을 계산하고 획득하는 클래스 */ class triangle implements figure_area { private /*double*/ $width; private /*double*/ $height; function __construct(/*double*/ $width, /*double*/ $height) { $this->width = $width; $this->height = $height; } public /*double*/ function get_area() { return ($this->width * $this->height) / 2; } } /** * 사각형 면적을 계산하고 획득하는 클래스 */ class rectangle implements figure_area { private /*double*/ $width; private /*double*/ $height; function __construct(/*double*/ $width, /*double*/ $height) { $this->width = $width; $this->height = $height; } public /*double*/ function get_area() { return $this->width * $this->height; } } /** * 원의 면적을 계산하고 획득하는 클래스 */ class circle implements figure_area { const /*double*/ pi = 3.1415927; private /*double*/ $radius; function __construct(/*double*/ $radius) { $this->radius = $radius; } public /*double*/ function get_area() { return self::pi * ($this->radius ^ 2); } } [/code]
< 각 도형의 면적을 계산하는 클래스 >
각 도형의 제원(가로, 세로 또는 반지름)은 아래의 예와 같이 생성자를 통해 전달됩니다.
[code php;gutter:false] $tri = new triangle(4, 5); $rect = new rectangle(4, 5); $circle = new circle(4); [/code]
삼각형과 사각형은 가로와 세로길이를 입력값으로, 원은 반지름을 입력값으로 지정합니다.
클래스 triangle, rectangle, circle의 생성자를 통해 각 도형의 제원이 전달되면 이 값이 각 멤버($width, $height, $radius)에 기록되며 get_area 메쏘드에서는 기록된 각각의 제원(가로, 세로 또는 반지름)을 가지고 각 도형에 맞는 면적을 계산하여 반환합니다.
이에 대한 실행 소스는 아래와 같습니다.
[code php;gutter:false] $obj_triangle = new calculate(); $obj_triangle->add(new triangle(4, 5)); $obj_triangle->add(new triangle(2, 6)); $obj_triangle->add(new triangle(5, 3)); print "==========================\n"; print "삼각형의 면적 통계\n"; print "--------------------------\n"; print '평균면적 =' . $obj_triangle->get_average() . "\n"; $max = $obj_triangle->get_maximum(); print '최대면적 =' . $max->get_area() . "\n"; print "==========================\n"; [/code]
< 여러개의 삼각형 면적의 평균값과 최대값 구하기 >
3행에서 "new triangle(4, 5)"에 의해 triangle 클래스의 객체가 생성되는 동시에 그 생성자를 통해 입력된 가로, 세로값인 4와 5가 triangle 클래스의 멤버변수 $width와 $height에 기록됩니다. 그리고 생성된 인스턴스는 바로 calculate 클래스로부터 생성된 $obj_triangle 인스턴스의 add 메쏘드의 입력값으로 전달됩니다. add 메쏘드에서 전달받은 인스턴스는 figure_area 인터페이스를 구현한 삼각형 클래스의 인스턴스가 됩니다.
결국 위 소스를 실행하게 되면 아래와 같은 결과를 얻을 수 있습니다.
==========================
삼각형의 면적 통계
--------------------------
평균면적 =7.83333333333
최대면적 =10
==========================
calculate 클래스가 오로지 figure_area 인터페이스만을 의존하여 작성하였지만 실제로 calculate 클래스의 add 메쏘드에는 figure_area 인터페이스를 구현한 각 도형 클래스의 인스턴스가 전달되므로 각 도형의 면적에 대한 평균값과 최대값을 구할 수 있게 됩니다.
figure_area 인터페이스를 구현하고 있는 각 도형의 클래스와 calculate 클래스를 UML(Unified Modeling Language)로 나타내면 아래와 같습니다(UML에 관한 것은 관련 서적을 살펴보기 바랍니다).
< figure_area 인터페이스 >
이 예제에 대한 전체소스와 실행결과를 아래에 첨부합니다.
[code php;gutter:false] <?php /** * 도형 면적값을 획득하는 인터페이스 */ interface figure_area { /*abstract public*/ /*double*/ function get_area(); } /** * 합, 평균값, 최대값을 구하는 클래스 */ class calculate { /*double*/ private $sum = 0; /*figure_area*/ private $maximum; /*integer*/ private $count = 0; public function add(/*figure_area*/ $x) { if ($x instanceof figure_area) { $this->sum += $x->get_area(); if ($this->count == 0 || $this->maximum->get_area() < $x->get_area()) { $this->maximum = $x; } $this->count++; } else { die("The variable x is not instance of figure_area\n"); } } public /*figure_area*/ function get_maximum() { return $this->maximum; } public /*double*/ function get_average() { return $this->sum / $this->count; } } /** * 삼각형 면적을 계산하고 획득하는 클래스 */ class triangle implements figure_area { private /*double*/ $width; private /*double*/ $height; function __construct(/*double*/ $width, /*double*/ $height) { $this->width = $width; $this->height = $height; } public /*double*/ function get_area() { return ($this->width * $this->height) / 2; } } /** * 사각형 면적을 계산하고 획득하는 클래스 */ class rectangle implements figure_area { private /*double*/ $width; private /*double*/ $height; function __construct(/*double*/ $width, /*double*/ $height) { $this->width = $width; $this->height = $height; } public /*double*/ function get_area() { return $this->width * $this->height; } } /** * 원의 면적을 계산하고 획득하는 클래스 */ class circle implements figure_area { const /*double*/ pi = 3.1415927; private /*double*/ $radius; function __construct(/*double*/ $radius) { $this->radius = $radius; } public /*double*/ function get_area() { return self::pi * ($this->radius ^ 2); } } $obj_triangle = new calculate(); $obj_triangle->add(new triangle(4, 5)); $obj_triangle->add(new triangle(2, 6)); $obj_triangle->add(new triangle(5, 3)); print "==========================\n"; print "삼각형의 면적 통계\n"; print "--------------------------\n"; print '평균면적 =' . $obj_triangle->get_average() . "\n"; $max = $obj_triangle->get_maximum(); print '최대면적 =' . $max->get_area() . "\n"; $obj_rectangle = new calculate(); $obj_rectangle->add(new rectangle(4, 5)); $obj_rectangle->add(new rectangle(2, 6)); $obj_rectangle->add(new rectangle(5, 3)); print "==========================\n"; print "사각형의 면적 통계\n"; print "--------------------------\n"; print '평균면적 =' . $obj_rectangle->get_average() . "\n"; $max = $obj_rectangle->get_maximum(); print '최대면적 =' . $max->get_area() . "\n"; $obj_circle = new calculate(); $obj_circle->add(new circle(4)); $obj_circle->add(new circle(2)); $obj_circle->add(new circle(5)); print "==========================\n"; print "원의 면적 통계\n"; print "--------------------------\n"; print '평균면적 =' . $obj_circle->get_average() . "\n"; $max = $obj_circle->get_maximum(); print '최대면적 =' . $max->get_area() . "\n"; print "==========================\n"; ?> [/code]
< 여러 도형 면적의 평균값과 최대값 구하기 >
==========================
삼각형의 면적 통계
--------------------------
평균면적 =7.83333333333
최대면적 =10
==========================
사각형의 면적 통계
--------------------------
평균면적 =15.6666666667
최대면적 =20
==========================
원의 면적 통계
--------------------------
평균면적 =13.6135683667
최대면적 =21.9911489
==========================
< 예제 실행 결과 >

Posted by 방글24
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