OVERRIDING INTERFACE METHODS IN SUBCLASSES
// 상속클래스에서 재정의 되는 인터페이스 메서드

내용에 앞서:

언급된 관련 서적 :

1. Don Box's Essential .NET Volume 1

번역 :
1.Polymorphism : 다형성, 다형적

2.Mathod signature : 메서드 서명, 메서드 식별자
(이를테며 void a(int i), void a(int i, float f) 오버로딩 함수라면 반환타입, 인자갯수, 인자 타입등..)

3.SubClass : 상속된 클래스

4.Override : 재정의

5.Implementation : 인터페이스의 최종 구현부 //(Java의 final)

내용 :

C#(일반적으로 닷넷)은 상속된 클래스 내부의 메소드들의 재정의을 위한 virtual/abstract와 계층적 구조를

 이용한 다형성을 제공합니다.

얼핏 보기에, C#은 그 개념에 있어서 C++과 Java 둘다의 특징을 보이지만, 그것은 피상에 불과합니다. 

C#에서 인터페이스를 정의는 다음과 같습니다.

public interface IFoo {

void doSomething();

}
코드참고1.

 


클래스A를 정의하여 인터페이스로 구현을 하게 됩니다.

public class A : IFoo {

public void doSomething() {

Console.WriteLine("In A.doSomething()");

}

}
코드참고2.

 

A를 IFoo 인터페이스로써 사용할 수 있습니다.

 

IFoo foo = new A();

foo.doSomething(); //outputs: In A.doSomething()
코드참고3.


기본적인 예로써, 이것이 가능한 것은 닷넷 런타임이 IFoo.doSomething()을 참조하는 곳에 A.doSomething()
을 찾아내며 오브젝트 참조의 메모리 출력되는 주소 공간을 조작하여 이루어 집니다.

이것은 언어에 따라 다르지만, 일반적으로 (간단하게) 개체에서 지원하는 각 유형의 구현에 매핑 개체에서 사용할 각 방법에 대한 주소 위치의 목록을 하나 이상 vtables를 사용하는것을 포함합니다.

가상 메서드들이 vtable로 컴파일 되는 시점에, 일반적인 메서드들은 자신들의 주소들을 가지는 코드안으로 컴파일이 되며,
이 포인터는 런타임 시점에서 언어가 가지는 다형성 규칙과 특정 참조에 따라 실제 메서드의 위치로 변환됩니다.
 일단
virtual 표기 되면, 함수의 메서드 서명은 타입 계층을 통한 virtual 유지됩니다

 virtual 표기 되면 메소드 서명에 대한 호출은 항상 어디 유형 계층 구조에 있는 참조 점에 관계없이 조회 vtable list 통해 이동 합니다.

 

인터페이스도 타입이므로, 정의된 인터페이스 메서드는 가상이기 때문에 이로써, 정의된 인터페이스 메서드는

재 정의가 될 수가 있습니다.

 C#에서(C++동일, Java제외) 정의된 가상 메서드들은 일반적으로 그들의 서명으로 표기되는 'virtual'키워드가 요구 됩니다.
 (그래서 컴파일러는 메서드의 실제 주소값 대신 추가된 vtable 진입점을 알고 있게 됩니다.)

 구현을 위해 인터페이스 메서드 서명을 상속하는 클래스들이 확실하게 요구하는 동안 인터페이스들의 요청은 잠시

보류가 됩니다.

(엄밀히 말해서 인터페이스들은 구현되는 클래스들의 기본 타입은 아니며, 이에 대한 자세한 정보는 
 Don Box's Essential .NET Volume 1 책의 70페이지를 활용바랍니다.)

   

또한 C#(C++동일,Java제외)에서 또다른 키워드 'override'는 메서드 서명의 vtable진입점으로 변환되기 위해 하위 클래스에서 요청을 하게 됩니다.

결과적으로, C#의 'sealed' 키워드(Java의 'final'과 동일)는 비-가상 메서드를 만들수 없게 만들지는 못해도,
하위 클래스에서 'sealed' 키워드가 선언된 인터페이스를 재 정의를 못하게 할 수는 있습니다.

 virtual 메서드 서명들은 상속 클래스에 vitual로 남게 되고, 인터페이스 메서드들이 virtual가 되면 인터페이스 메서드로써

상속 클래스들의 클래스들에서 구현 부 인터페이스들로써의 재정의가 가능하게 됩니다.
 이하 코드는 이상 내용의 참고:

public class A : IFoo {

public void doSomething() {

Console.WriteLine("In A.doSomething()");

}

}
코드참고4.

public class B : A {

public override void doSomething() {

Console.WriteLine("In B.doSomething()");

}

}
코드참고5.

C#에서 동작하지 않고 다음 에러문구를 출력합니다:

B.doSomething()' : cannot override inherited member 'A.doSomething()'

because it is not marked virtual, abstract, or override

//B.doSomething() : A.doSomething()를 구성요소로써 재정의 할 수 없습니다.
A.doSomething()에 virtual, abstract 또는 override 표시가 없기 때문입니다.//

A를 간단히 해서 'override' 를 메서드 정의에서 제외 시키면 다음과 같이 출력됩니다.

public class B : A {

public void doSomething() {

Console.WriteLine("In B.doSomething()");

}

}
코드참고6.


컴파일 되지만 원하는 결과가 또한 아니며 다음의 경고 메세지를 알려주게 됩니다.:

The keyword new is required on 'B.doSomething()' because it hides inherited member 'A.doSomething()'
new 키워드가 'B.doSomething()'에 필요합니다. 상속 상위 개체 함수'A.doSomething()'에 의해 숨겨졌습니다.

"상속 상위 개체 맴버에의해 숨겨지다" 라는 의미를 해석해 보자면
 C#(C++동일,Java제외)은 저러한 기반 클래스들의 동일한 서명을 가진 상속클래스들의 메소드들을 허용합니다.
 여기서 다시 상기 해보자면 virtual 키워드로 구현된 것이 아니라면 'vtable'목록에 포함되지 않습니다. 
 쉽게 말하자면 B는 두 개의 doSomething()메서드를 가지게 됩니다.
 결과 인식 메서드는A로부터 상속 받은 것, 그리고 B에서 새로 정의한 것 2개가 되며, 그런 이유로 new 키워드가
새로 필요하다는 경고 메세지를 출력하게 됩니다.
 또한 다음에서 보듯, IFoo로써 B를 사용하는 것은 의도하는 바가 아닙니다.

IFoo foo = new B();

foo.doSomething(); //outputs: In A.doSomething() // 원하는 결과가 아님.

한가지 해결점은 'virtual' 메서드를 A.doSomething()앞에 표시하는 것이지만 만약 A 코드에 접근할 수 없는 상황이라면
어떻게 해야할까요?





C#상에서 모든 것을 충족시키는 IFoo의 구현부인 B로써는 다음과 같습니다:

public class B : A, IFoo {

void IFoo.doSomething() {

Console.WriteLine("In B.doSomething()");

}

}

IFoo foo = new B();

foo.doSomething(); //outputs: In B.doSomething()

코드참고7.

그러나 위 사항은 저장 영역으로 B는 다른 syntax임을 주목 해야 합니다.
'public' 키워드를 제외시킴으로써 IFoo.doSomething의 구현을 명확하게 할 수가 있습니다. 
 이전과 같은 경고와 에러가 뜨지 않게 하려면, A에서 했던 것과 같은 단순화된 메소드를 구현할 수는 없습니다. 
이것은 외부 구현부에서 변경하여, 이 클래스 선언을 통해 B를 private 하게 하고 public 키워드를 삭제할 필요가 있습니다.
B에서 구현된 doSomething()은 인스턴스화 된 B에서는 접근 안되며, IFoo에서만 가능합니다.

   

B b=newB();
A a =b;

b.doSomething(); //outputs: In A.doSomething() !!
a.doSomething(); // 역시나 위와 동일한 결과


Essential .NET Volume 1 page 169:

...the implementation of an interface method is implicitly 'final', just as if
the 'sealed' and 'override' modifiers were present.

...인터페이스 메서드의 구현부 에서 선언은 'final'이며, 'sealed'나 'override' 또한 제공됩니다.

인터페이스 메서드를 구현하기 위해서는 sealed 키워드를 사용 해야 하며,
 다르게 보자면 C#에서는 첫 번째 인터페이스 마지막 구현부 에서는 override 로 연결되는 것입니다.

 
C#(.NET)은 뚜렷한 base class designer(기반 클래스 디자이너)의 디자인 결정을 해야 하는 일종에 디자인 철학을

준수합니다.

 어떤 클래스들이 확장 되고자 한다면 명백하게 표기가 되어야 합니다.
반대로 말하면, C++은 무엇인가가 명백화 해줄 필요가 있는, 또한 자바가 그러한, 최종 결과를 지어줄 수 있는 그러한

클래스 디자이너가 제공되지 않고 기본적으로 모든 메서드 들은 가상화 됩니다.
 이것은 보다 많은 토론의 여지가 있는 주제 이지만 필자의 생각으로는 이것은 가능한 것과 직관적인 것 사이에 있어 디자인적인 선호도로 부터 파생된 결과라고 생각합니다.
 필자는 기술적으로 가능한 디자인의 사용을 더욱 선호 합니다.

그러나, 언어 디자인 선택에 관해 논쟁을 펼치는 것은 "돈키호테"와 같다고 믿기 때문에, 근래 들어 개인적으로 선호 하는 것 은 진행해 보고, 함정들을 표시하고, 왜 그것들이 거기 있는지 이해하고, 그것들을 옮기는 방식입니다.

'Programming > Design' 카테고리의 다른 글

OCP : The Open-Closed Principle  (0) 2011.08.05
SRP : The Single Responsibility Principle  (0) 2011.07.18

+ Recent posts