개요: 이 기사에서는 다형성의 개념과 객체지향 설계에서의 적용에 대해 설명합니다. 또한 PHP 5에서 다형성을 사용하는 방법과 그 장점과 단점을 분석합니다.
최신 릴리스의 PHP에서는 후기 바인딩에 대한 지원이 구현되었습니다. 물론 Late Binding 기능을 사용하는데 있어서 여전히 문제점이 많습니다. 이전 버전의 PHP를 사용하는 경우(내 서버는 PHP 5.0.1을 실행 중임) 후기 바인딩에 대한 지원이 부족할 수 있습니다. 따라서 이 문서의 코드는 특정 버전의 PHP 5에서 작동하지 않을 수 있습니다.
1. PHP 5와 다형성
이 기사에서는 객체 지향 프로그래밍의 가장 중요한 부분 중 하나인 다형성 설계에 대해 논의하고 싶습니다. 문제를 설명하기 위해 PHP 5를 사용하고 있습니다. 계속 읽기 전에 이 기사가 전적으로 PHP에 관한 것이 아니라는 점을 분명히 하십시오. 언어가 지난 두 가지 주요 버전에 비해 빠른 개발을 통해 큰 진전을 이루었지만 C++ 또는 Java 코스와 같은 보다 성숙한 언어와 경쟁하려면 아직 시간이 좀 걸립니다.
객체 지향 프로그래밍의 초보자라면 이 기사가 적합하지 않을 수 있습니다. 다형성의 이 부분은 특별하기 때문입니다. 일단 이해하고 나면 결코 잊지 못할 것입니다. 객체 프로그래밍과 디자인에 대해 조금 배우고 싶지만 누군가 "객체가 다형성이다"라고 말하는 것이 무엇을 의미하는지 잘 모른다면 이 기사가 도움이 될 것입니다.
이 기사를 마치면 다형성이 무엇인지, 이를 객체지향 설계에 어떻게 적용하는지 알게 될 것이며, PHP 5에서 객체 프로그래밍의 장점과 단점을 이해하게 될 것입니다.
2. 다형성이란 무엇입니까?
Dictionary.com의 다형성에 대한 정의는 "독립적인 조직이나 동일한 조직 내에서 근본적인 차이 없이 서로 다른 형태, 단계 또는 유형으로 발생하는 것"입니다. 이 정의에서 우리는 다형성 성은 동일한 것을 설명하는 프로그래밍 방식이라고 생각할 수 있습니다. 여러 상태 또는 단계를 통해 객체를 생성합니다. 실제로 그 진짜 의미는 실제 개발에서는 인터페이스나 기본 클래스의 프로그래밍에만 집중하면 되고 객체가 속한 특정 클래스(클래스)에 대해서는 걱정할 필요가 없다는 것입니다.
디자인 패턴에 대해 잘 알고 계시다면, 비록 사전 지식만 갖고 계시더라도 이 개념을 이해하실 수 있을 것입니다. 실제로 다형성은 패턴 기반 디자인 프로그래밍에서 가장 훌륭한 도구일 수 있습니다. 이를 통해 유사한 객체를 논리적인 방식으로 구성할 수 있으므로 코딩할 때 객체의 특정 유형에 대해 걱정할 필요가 없으며 원하는 인터페이스나 기본 클래스만 프로그래밍하면 됩니다. 애플리케이션이 추상화될수록 유연성은 더욱 높아집니다. 다형성은 동작을 추상화하는 가장 좋은 방법 중 하나입니다.
예를 들어 Person이라는 클래스를 생각해 보겠습니다. David, Charles 및 Alejandro라는 클래스를 사용하여 Person을 하위 클래스로 분류할 수 있습니다. Person에는 추상 메서드 AcceptFeedback()이 있으며 모든 하위 클래스는 이 메서드를 구현해야 합니다. 이는 기본 Person 클래스의 하위 클래스를 사용하는 모든 코드가 AcceptFeedback() 메서드를 호출할 수 있음을 의미합니다. 객체가 David인지 Alejandro인지 확인할 필요가 없으며 그것이 Person이라는 것을 아는 것만으로도 충분합니다. 결과적으로, 코드는 "최소 공통 분모"인 Person 클래스에만 초점을 맞춰야 합니다.
이 예제의 Person 클래스는 인터페이스로 생성될 수도 있습니다. 물론 위와 비교하면 몇 가지 차이점이 있습니다. 주로 인터페이스는 어떤 동작도 제공하지 않고 일련의 규칙만 결정한다는 것입니다. Person 인터페이스에는 "AddFeedback() 메서드를 지원해야 합니다"가 필요한 반면, Person 클래스는 AddFeedback() 메서드에 대한 일부 기본 코드를 제공할 수 있습니다. 이에 대한 이해는 "AddFeedback() 지원을 선택하지 않으면 다음과 같습니다. 기본 구현을 제공해야 합니다. "인터페이스 또는 기본 클래스를 선택하는 방법은 이 문서의 주제를 벗어납니다. 그러나 일반적으로 기본 클래스를 통해 기본 메서드를 구현해야 합니다. 클래스가 구현하는 원하는 기능 세트를 간단하게 설명할 수 있는 경우 인터페이스를 사용할 수도 있습니다.
3. 다형성 디자인 적용
계속해서 Person 기본 클래스의 예를 사용하고 이제 비다형성 구현을 분석해 보겠습니다. 다음 예제에서는 다양한 유형의 Person 개체를 사용합니다. 이는 매우 만족스럽지 못한 프로그래밍 방식입니다. 실제 Person 클래스는 생략되었습니다. 지금까지 우리는 코드 호출 문제에만 관심을 가졌습니다.
<?php
$이름 = $_SESSION['이름'];
$myPerson = 사람::GetPerson($name);
스위치(get_class($myPerson)){
사례 'David':
$myPerson->AddFeedback('훌륭한 기사!', '어떤 독자', date('Ym-d'));
부서지다;
사례 '찰스':
$myPerson->feedback[] = array('어떤 독자님', '훌륭한 편집!');
부서지다;
사례 '알레한드로':
$myPerson->Feedback->Append('멋진 자바스크립트!');
부서지다;
기본 :
$myPerson->AddFeedback('예!');
}
?>
이 예제는 서로 다른 동작을 가진 개체를 보여 주며, 각각의 올바른 작업을 수행하기 위해 switch 문을 사용하여 서로 다른 Person 클래스 개체를 구별하는 데 사용됩니다. 여기에 있는 피드백 설명은 조건에 따라 다릅니다. 실제 애플리케이션 개발에서는 그렇지 않을 수도 있습니다. 저는 단순히 클래스 구현에 존재하는 차이점을 설명합니다.
아래 예에서는 다형성을 사용합니다.
<?php
$이름 = $_SESSION['이름'];
$myPerson = 사람::GetPerson($name);
$myPerson->AddFeedback('훌륭한 기사!', 'SomeReader', date('Ym-d'));
?>
여기에는 스위치 문이 없으며 가장 중요한 것은 Person::GetPerson()이 반환할 개체 유형에 대한 정보가 부족하다는 점입니다. 그리고 다른 Person::AddFeedback()은 다형성 메서드입니다. 동작은 구체적인 클래스로 완전히 캡슐화됩니다. 여기서 David, Charles 또는 Alejandro를 사용하든 호출 코드는 구체적인 클래스의 기능을 알 필요가 없으며 기본 클래스만 알 수 있습니다.
내 예제가 완벽하지는 않지만 호출 코드의 관점에서 다형성의 기본 사용을 보여줍니다. 이제 이러한 클래스의 내부 구현을 분석해야 합니다. 기본 클래스에서 파생할 때 가장 좋은 점 중 하나는 파생 클래스가 부모 클래스의 동작에 액세스할 수 있다는 것입니다. 이는 종종 기본 구현이지만 더 복잡한 동작을 생성하기 위해 클래스 상속 체인에서 발생할 수도 있습니다. 다음은 이 상황에 대한 간단한 데모입니다.
<?php
클래스 사람{
함수 AddFeedback($comment, $sender, $date){
//데이터베이스에 피드백 추가}
}
클래스 David는 Person{을 확장합니다.
함수 AddFeedback($comment, $sender){
상위::AddFeedback($comment, $sender,
date('Ym-d'));
}
}
?>
여기서는 David 클래스의 AddFeedback 메서드 구현에서 Person::AddFeedback 메서드가 먼저 호출됩니다. C++, Java 또는 C#의 메서드 오버로드를 모방한다는 것을 알 수 있습니다. 이것은 단지 단순화된 예일 뿐이며 작성하는 실제 코드는 실제 프로젝트에 완전히 의존한다는 점을 명심하십시오.
4. PHP 5의 후기 바인딩
제 생각에는 후기 바인딩이 Java와 C#이 그토록 매력적인 이유 중 하나입니다. 기본 클래스 메서드가 "this" 또는 $this를 사용하여 메서드를 호출할 수 있도록 허용합니다(기본 클래스에 존재하지 않거나 기본 클래스의 메서드 호출이 상속된 클래스의 다른 버전으로 대체될 수 있는 경우에도 마찬가지). PHP에서 허용되는 다음 구현을 고려할 수 있습니다.
<?php
클래스 사람{
함수 AddFeedback($messageArray) {
$this->ParseFeedback($messageArray);
//데이터베이스에 쓰기}
}
클래스 David는 Person{을 확장합니다.
함수 ParseFeedback($messageArray){
// 분석 수행}
}
?>
Person 클래스에는 ParseFeedback이 없다는 것을 기억하세요. 이제 구현 코드의 이 부분이 있다고 가정하면(이 예를 위해) $myPerson이 David 객체가 됩니다.
<?php
$myPerson = 사람::GetPerson($name);
$myPerson->AddFeedback($messageArray);
?>
분석 오류가 발생했습니다! 일반적인 오류 메시지는 ParseFeedback 메서드가 존재하지 않거나 유사한 정보가 있다는 것입니다. PHP 5의 후기 바인딩에 대해 이야기해 봅시다! 다음으로 후기 바인딩의 개념을 요약해 보겠습니다.
후기 바인딩은 메서드 호출이 마지막 순간까지 대상 개체에 바인딩되지 않음을 의미합니다. 이는 런타임 시 메서드가 호출될 때 해당 개체에 이미 구체적인 유형이 있음을 의미합니다. 위의 예에서는 David::AddFeedback()을 호출했으며 David::AddFeedback()의 $this가 David 객체를 참조하므로 ParseFeedback() 메서드가 존재한다고 논리적으로 가정할 수 있지만 실제로는 그렇지 않습니다. AddFeedback()이 Person에 정의되어 있고 ParseFeedback()이 Person 클래스에서 호출되기 때문에 존재합니다.
불행하게도 PHP 5에서는 이러한 동작을 제거하는 쉬운 방법이 없습니다. 이는 유연한 다형성 클래스 계층 구조를 생성하려는 경우 약간 무력할 수 있음을 의미합니다.
나는 이 기사에서 PHP 5를 표현 언어로 선택했다는 점을 지적해야 합니다. 왜냐하면 이 언어는 객체 개념의 완벽한 추상화를 실현하지 못하기 때문입니다! 이는 PHP 5가 아직 베타 버전이기 때문에 이해할 수 있습니다. 또한 이제 추상 클래스와 인터페이스가 언어에 추가되었으므로 런타임 바인딩도 구현해야 합니다.
5. 요약
이 시점에서 다형성이 무엇인지, 그리고 왜 PHP 5가 다형성을 달성하는 데 완벽하지 않은지에 대한 기본적인 이해가 있어야 합니다. 일반적으로 조건부 동작을 캡슐화하기 위해 다형성 개체 모델을 사용하는 방법을 알아야 합니다. 물론 이렇게 하면 개체의 유연성이 향상되고 구현할 코드가 줄어듭니다. 또한 특정 조건(객체 상태에 따라 다름)을 충족하는 동작을 캡슐화하여 코드의 명확성을 높입니다.