다형성은 요구사항 변화에 대한 소스 코드의 충격을 완화하는데 상당히 중요한 도구 입니다. 그리고, 이것을 구현하기 위해서는 클래스 상속과 인터페이스 구현이라는 방식을 사용하게 됩니다. (다른 방법이 전혀 없는 것은 아니지만) 그런데, 그 둘의 차이점은 무엇일까요? 구구 절절하게 쓰면, 이해하기 어려워 질 수 있으니 중요한 차이점만 살펴보도록 하겠습니다.
Class |
Interface |
코드의 공유 제공 |
코드의 공유를 제공하지 않음 |
다중상속을 지원하지 않음 (일부 언어 제외) |
다중상속이 가능 함 |
코드 공유를 통해서 얻어지는 이점은 중복 코드의 제거와 변화의 자동 적용 입니다. 그리고, 이것으로 인해서 얻어지는 부작용이 바로 변화의 파급효과 입니다.
변화에 대한 자동 적용이라는 것은 간단합니다. [그림 1]에서 기반 클래스에 해당하는 관리자의 "로그인" 메소드가 원래는 오라클 데이터베이스를 이용해서 구현되었다가, MySQL로 변경되었다고 가정하겠습니다. 이때, 관리자의 클래스의 "로그인" 메소드를 수정하는 것만으로 모든 하위 클래스들은 그 변화를 그대로 적용 할 수가 있습니다. 또한, 만약에 관리자가 공지를 올릴 수 있도록 기능을 추가한다고 했을 때, 역시 관리자 클래스에 "공지작성"이라는 메소드만 추가하면 모든 종류의 관리자 클래스에 바로 적용이 됩니다.
[그림 1] 쇼핑몰 관리자에 대한 Class Diagram
그런데, 이러한 이점이 항상 즐거운 것만은 아니라는 것이 문제입니다. 만약 업무 특성상 제품관리자는 공지를 작성 할 수 없다면 어떻게 될까요? 제품관리자는 해당 메소드를 호출하지 못하도록 다른 조치를 해야 할 필요가 생기게 됩니다.
"내부에 권한 플래그를 두면 되지."
"상속 받은 쪽에서 override를 통해서 무시하거나 Exception을 발생 시키면 되지."
라고 생각하는 분이 혹시라도 계신다면, 이건 다소 위험한 발상입니다. 이에 대해서는 나중에 여유 있을 때 정리하도록 하겠습니다. (제가 잊어 먹고 지나가지 않는다면 ㅡ.ㅡ)
기반 클래스의 변화가 모든 파생 클래스에게 영향을 주기 때문에, 설계 시 고려하지 못했던 요구사항의 변화가 왔을 때, 그로부터 파생 된 모든 클래스에게 그 영향이 파급된다는 점은 심각한 문제가 될 수 있습니다. 또한, 리스코프 대체의 원칙을 위반하게 됩니다.
가장 심각한 문제는 어떤 변화가 있을 지 모르기 때문에 이것을 효과적으로 대처 할 수가 없다는 것입니다.
정리하면, "코드 공유를 통해서 얻어지는 이점이 있는 가 하면, 그에 따른 부작용도 있다" 입니다.
일단 클래스의 상속은 생각보다 비용이 큽니다. 상속보다는 위임을 사용하라는 말을 수 없이 듣게 됩니다. 그리고, 다형성이 필요할 때도, 클래스 상속보다는 인터페이스 구현을 사용하라고 합니다.OOP에서 상속은 일단 피할 수 있으면 피하는게 유리합니다.
그럼 어떻게 할까요? 상속은 무시하고 인터페이스 구현만으로 코딩을 해야 할까요? 결론은 상황에 따라서 적절한 선택을 해야 한다는 것 입니다. 참으로 무책임한 말이긴 하지만, 어쩔 수 없는 현실입니다.그 선택을 하는데 가장 중요한 기준이 바로, 두 객체 사이의 공통점이 같은 종(種)으로서의 유전적인 유사성이냐 또는 종(種)과 상관없이 후천적인 획득 형질의 중복이냐를 구별하는 것 입니다.말이 어려워졌으니 다시 설명을 드립니다. 설계 이후부터 기반 클래스의 변화가 파생 클래스에게 그대로 이어져도 상관없을 듯 한 자연스러운 유사성이라면 이것을 유전적인 형질과 동일하게 취급할 수 있다는 뜻 입니다. 또, 다시 첨언 한다면, 설계가 튼튼해서 앞으로도 크게 문제가 없을 듯 한 것들은 상속을 이용한다고 해도 큰 문제가 없고, 오히려 상속을 사용해야 코드가 간결해지고 효율적이라는 이야기 입니다.처음 설계 할 때에는 전혀 고려 할 수도 없었던 변화가 생기거나 예측이 된다면, 상속보다는 인터페이스를 사용해야 하고, 이 것들은 후천적인 형질, 즉, 후천적인 문제로 간주 할 수 있다는 뜻 입니다.상속을 받아서 따로 추가한 기능과 같은 후천적인 문제는 아무런 문제가 될 것이 없습니다. 그냥 그렇게 하면 됩니다. 문제는 이러한 것들이 중복되는 경우입니다. 즉, 복수의 객체가 자신의 종과 상관이 없는 기능이 추가되었는데, 이것이 서로 중복이 될 경우 입니다."그 기반 클래스와 부류(파생 클래스)들은 당연히 그래" 라고 할 수 있다면, 그 모든 클래스들은 같은 종이 됩니다. 그리고, 그 기반 클래스와 파생 클래스의 관계는 "Is-a"와 같은 관계에 있다고 할 수 있습니다. 동일한 종(種)으로 취급을 받는 것 입니다."Is-a" 관계를 나타내는 것은 상속이 자연스럽습니다.예를 들면, 나도 사람이고 아내도 사람입니다. 우리는 같은 종(種)입니다. 그래서, 배도 고프고, 짜증을 낼 때도 있고, 아프기도 합니다. 이런 것들은 너무나 당연한 유전적인 형질들입니다. 상속되는 것이죠. 그런데, 저는 프로그래밍을 할 수 있지만, 아내는 컴퓨터 다루는 것 자체를 어려워 합니다. 그리고, 제 동료들은 역시 프로그래밍을 잘 합니다. 이러한 형질들은 사람이니까 당연히 할 수 있는 것이 아닙니다. 획득된 형질들이며, "Can-do"로 분류 할 수 있는 것 들 입니다."Can-do" 의 특징을 같는 것들은 인터페이스 구현이 자연스럽습니다.문제는 소프트웨어 개발의 관점에서는 경우에 따라서 유전적 형질에 대한 판단이 달라진다는 것 입니다. 만약 프로그래머가 핵심 관점이라면, 프로그래밍 능력은 유전적인 형질로 취급 될 수 있습니다. 더구나, 요구사항 변화로 인해 다양한 관점에서 접근하게 된다면, 이러한 기준이 지속적으로 흔들릴 수도 있습니다. 이처럼 현실과 코드 사이의 차이는 상당합니다. 이부분은 설계자의 재량이 필요한 부분이라고 할 수 있겠습니다.물론 다중상속을 통해서 해결할 수도 있습니다. 하지만, 다중상속은 단일상속에 비해서 코드를 어렵게하는 단점이 너무 뼈 아픕니다. 더구나, 다중상속을 지원하지 않는 언어가 상당히 많기 때문에 완벽한 해결책이라고 할 수 없습니다.
[그림 2] 요구사항 분석 이후
[그림 3] 중복 코드의 정규화 이후
[그림 4] 새로운 요구사항의 발생
[그림 5] 쉬운 해결책
[그림 6] 후천적 형질은 인터페이스로 구현하자
[그림 7] 더욱 추가된 요구사항
unit Base;
interface
uses
Classes, SysUtils;
type
육상동물행동 = interface
['']
procedure 기어다니기;
end;
수상동물행동 = interface
['']
procedure 헤엄치기;
end;
육상동물행동클래스 = class (TInterfacedObject, 육상동물행동)
private
public
procedure 기어다니기;
end;
수상동물행동클래스 = class (TInterfacedObject, 수상동물행동)
private
public
procedure 헤엄치기;
end;
애완동물 = class
private
public
procedure 사료먹기;
end;
강아지 = class(애완동물, 육상동물행동)
private
_육상동물행동 : 육상동물행동클래스;
property AnimalBehavior : 육상동물행동클래스 read _육상동물행동
implements 육상동물행동;
public
end;
고양이 = class(애완동물, 육상동물행동)
private
_육상동물행동 : 육상동물행동클래스;
property AnimalBehavior : 육상동물행동클래스 read _육상동물행동
implements 육상동물행동;
public
end;
금붕어 = class(애완동물, 수상동물행동)
private
_수상동물행동 : 수상동물행동클래스;
property AnimalBehavior : 수상동물행동클래스 read _수상동물행동
implements 수상동물행동;
public
end;
implementation
{ 육상동물행동클래스 }
procedure 육상동물행동클래스.기어다니기;
begin
end;
{ 수상동물행동클래스 }
procedure 수상동물행동클래스.헤엄치기;
begin
end;
{ 애완동물 }
procedure 애완동물.사료먹기;
begin
end;
end.
Jow Flow 작성의 기초 (0) | 2011.12.14 |
---|---|
Job Flow의 소개 (1) | 2011.12.14 |
데이터베이스 설계의 기본 원리 (0) | 2011.02.12 |
개발자의 분류 - 추상화 래밸 편 (0) | 2010.09.09 |
Job Flow의 소개 (0) | 2010.03.23 |