본문 바로가기

.NET/C#

Interface

728x90
반응형

인터페이스에 대해서 알아볼까합니다. 우선 걱정이 앞서는데요 제가 C#을 공부하면서 제일 거부감 느끼고 여려워 했던 부분중에 하나라서 과연 제대로 글을 써질지, 이상한 소리만 하다가 끝나는건 아닌지 걱정되네요. 그래도 도움을 드릴 수 있다는 생각을 용기내어 열심히 나아가보도로 하죠!!!!!

인터페이스 그것이 과연 무엇일까요? 인터페이스는 추상적인 멤버들을 의미상으로 연결한 모음이라고 할 수 있지요. 단지 추상화된 멤버들의 집합 이것이 인터페이스 자체인것입니다.
그럼 또 이런 의문점이 생기게 됩니다. 그럼 그냥 abstract class를 쓰면되지 귀찮게 interface란걸 쓰냐 이런 의문점이죠. abstract class의 경우는 일반적으로 덩치가 커지게 됩니다. public, private, protected등의 상태 데이터도 가질 수 있고요. 하지만 인터페이스는 순수 프로토콜인거죠. 그래서 인터페이스는 절대로 데이터 타입을 정의할 수가 없습니다. 당연히 메소드의 디폴트 구현도 없는거죠. 결국 인터페이스의 모든 멤버는 자동으로 추상화가 되는것입니다. 게다가 C#은 클래스 다중 상속을 지원하지 않기 때문에 인터페이스가 중요성을 끼치게 되는 것입니다.

그럼 인터페이스의 전체적인 특성과 형식에 대해서 알아보겠습니다.
1. 인터페이스의 형식 : interface 이름 {....}   /이름은 관행적으로 대문자 I로 시작합니다.
2. 인터페이스는 field를 포함하지 않습니다.
3. 모든 method는 abstract 형식입니다. abstract를 붙이는건 아니고 그냥 리턴형과 이름만 가지게 됩니다.
 예) void Method();
4. 객체를 생성할 수 없습니다. / 오직 참조 형식으로만 사용할 수 있죠
5. 멤버에 접근지정자를 사용할 수 없습니다. 즉 public/protected/private 같은 지정자를 못쓰죠.
6. interface는 class에 의해 구현되어 사용됩니다.
   -형식 : class이름 : 구현할 인터페이스 {......}
   -이때 interface를 참조로 객체에 접근한 경우 interface에 선언된 메소드만 접근할 수 있습니다.
7. interface를 구현하는 class는 interface의 모든 메소드를 정의(구현)해야 합니다.
8. 다중인터페이스 구현이 가능합니다. ->하나의 클래스가 두개 이상의 인터페이스를 구현 가능한거죠
  -형식 : class 이름 : interface, interface2, ...{...}
9. 클래스상속과 인터페이스구현이 동시에 표현할 경우 클래스상속을 먼저 표현해야 합니다.
10. 다중인터페이스 구현시 두개 이상의 구현 대상 인터페이스에 같은 메서드가 있을 경우 명시적 인터페이스 구현을 사용해야 합니다.
11. 명시적 인터페이스 구현에 접근지정자를 사용할 수 없습니다.
12. 인터페이스간에 상속이 가능합니다.

인터페이스의 문법상 규칙은 위의 것과 같습니다. 더 있는데 빼묵었나? ㅎㄷㄷ
뭐 문법적 체계는 저렇고요. 문제는 직접구현이겠죠. 솔직히 뭐 간단한 인터페이스 구현 및 사용하는거 쯤이야 심하게 어렵지는 않겠죠 근데 막 저걸 어쩔때 써야 할지 어떻게 써야할지 할때 많이 난감하드라구요 저는 ㅜㅜ

그럼 이제 인터페이스 구현 방법과 사용법 등을 보도록 하지요

    1 interface IDummyBase

    2 {

    3     void BaseMethod();

    4 }
보시는것과 같이 인터페이스 안에는 필드가 들어갈 수 없습니다. 그리고 메소드에는 접근지정자를 쓸수가 없고요. 진짠가?? 라고 생각하시는분들 한번 넣어보시고 컴파일해보시길 ^^

    1 interface IDummyBase

    2 {

    3     void BaseMethod();

    4 }

    5 interface IDummy : IDummyBase //인터페이스간 상속

    6 {

    7     void Method();

    8     void MethoA();

    9 }

   10 interface IDummy2

   11 {

   12     void Method();

   13     void MethodB();

   14 }

인터페이스간의 상속도 물론 가능합니다. 이때 클래스간의 상속과 틀린것은 부모 인터페이스의 메소드를 재정의 하지 않는다는 것이지요(당연한걸까요?)

    1 class Dummy : IDummy, IDummy2

    2 {

    3     public void BaseMethod() { }

    4     void IDummy.Method() //명시적 인터페이스 구현

    5     {

    6         Console.WriteLine("IDummy.Method 구현");

    7     }

    8     void IDummy2.Method() //명시적 인터페이스 구현

    9     {

   10         Console.WriteLine("IDummy2.Method 구현");

   11     }

   12     public void MethodA()

   13     {

   14         Console.WriteLine("IDummy.MethodA 구현");

   15     }

   16     public void MethodB()

   17     {

   18         Console.WriteLine("IDummy2.MethodB 구현");

   19     }

   20 }

클래스에서는 역시 인터페이스의 다중 상속이 가능했고요. 메소드의 명시적인 구현에서는 접근지정자를 붙일수 없었습니다. 또한 저 메소드 중에서 하나라도 빠지면 안되겠지요. 인터페이스의 모든 클래스가 다 구현이 되어야지만 실행이 가능하다는 이야기 이지요 ^^ 이제 메인에와서 테스트를 해보겠습니다.

    1 IDummy dummy = new IDummy();//오류

네!! 이렇게 입력하신다면 100%니다. 인터페이스로는 객체를 생성할 수 없다고 하죠 그래서 에러가 나는 것입니다.

    1 IDummy dummy = new Dummy();

    2 dummy.MethodA();

다음과 같이 입력하고 테스트를 한다면 간단히 답이 나오겠죠.

아까 Dummy 클래스에서 재정의 했던 MethodA의 내용이 잘 나오는걸 볼수 있습니다.

    1 Dummy dummy = new Dummy();

    2 

    3 IDummy dummy1 = dummy;

    4 dummy1.MethodA();

    5 

    6 IDummy2 dummy2 = dummy;

    7 dummy2.MethodB();

    8 

    9 dummy1.Method();

   10 dummy2.Method();

위의 코드는 interface를 참조로 객체에 접근할 경우를 테스트한 것인데요 결과를 보시면 확실히 이해가 가실겁니다.

Dummy는 모든 메소드를 다가지고 있었지만 인터페이스 참조로 인해서 자기 인터페이스에 선언된 메소드만 접근할 수 있게 되는 겁니다. 그래서 위와 같은 결과가 나오게 되는 것이지요.
이제 인터페이스의 장점이라고 할까 좋은점이라고 할까 암튼 꽤 괜찮은 점을 소개 해드리죠. 우리 클래스만을 배웠을 때를 생각해보고 코드를 작성해보겠습니다. 어떤 서비스가 있을 때 그 비슷하지만 서로 다른 결과물을 내놓는다고 생각하죠. 그것을 클래스로 구성해서 사용할때 다음과 같을 겁니다.

    1 class ServiceProviderA

    2 {

    3     public void DoServiceA()

    4     {

    5         Console.WriteLine("ServiceA");

    6     }

    7 }

    8 class ServiceProviderB

    9 {

   10     public void DoServiceB()

   11     {

   12         Console.WriteLine("ServiceB");

   13     }

   14 }
결과를 보기 위해서는 다음과 같이 구현하시면 됩니다

    1 ServiceProviderA spa = new ServiceProviderA();

    2 spa.DoServiceA();

    3 ServiceProviderB spb = new ServiceProviderB();

    4 spb.DoServiceB();

각각 호출하기 위해서는 메소드 A,B를 다 호출해야 될 것입니다. 그런데 작동원리나 생김새는 비슷하죠 이때 인터페이스를 사용하면 좀 더 간편해 질수 있습니다.

    1 interface IService

    2 {

    3     void DoService();

    4 }

    5 class ServiceProviderA : IService

    6 {

    7     public void DoService()

    8     {

    9         Console.WriteLine("ServiceA");

   10     }

   11 }

   12 class ServiceProviderB : IService

   13 {

   14     public void DoService()

   15     {

   16         Console.WriteLine("ServiceB");

   17     }

   18 }

다음과 같이 정의하면 보시는봐와 같이 달라진 것은 없지만 각 클래스의 메소드 이름이 똑같아진다는 것입니다.

    1 ServiceProviderA spa = new ServiceProviderA();

    2 spa.DoService();

    3 ServiceProviderB spb = new ServiceProviderB();

    4 spb.DoService();

이렇게 하시면 결과를 보실 수 있으시죠. 솔직히 차이가 거의 없습니다. 근데 왜 이렇게 해야되나 원래되로 하는게 더 편한거 아냐? 라시는 분들 분명 있습니다. 지금 예문은 쉽게 설명을 위할 뿐이고 실제로는 저렇게 안쓰입니다. 저렇게 쓸바에 그냥 클래스로 만들고 사용하는게 났죠. 언제 어디로가 딱 말씀드리기 힘들지만 분명 써야될 경우가 발생할 것입니다. 그때를 찾아 잘 쓰는게 개발자의 역량이겠지요 ^^(저는 잘 못한답니다 ㅜㅜ그래서 더 답답한 초보의 맘이겠죠 ㅜㅜ)
다음으로 IComparable 인터페이스에 대해서 알아보려고 합니다.

    1 class Item

    2 {

    3     public int Number { get; set; }

    4     public string Name { get; set; }

    5 

    6     public override string ToString()

    7     {

    8         return string.Format("{0}, [{1}]", Number, Name);

    9     }

   10 }

다음과 같이 정의 하고 메인을 다음과 같이 구현하고 실행을 해보세요

    1 static void Main(string[] args)

    2 {

    3     Random r = new Random();

    4     Item[] items = new Item[10];

    5     for (int i = 0; i < items.Length; i++)

    6     {

    7         items[i] = new Item

    8         {

    9             Number = r.Next(100, 1000),

   10             Name = "아이템 " + r.Next(100, 1000)

   11         };

   12     }

   13     for (int i = 0; i < items.Length; i++)

   14     {

   15         Console.WriteLine(items[i]);

   16     }

   17     System.Array.Sort(items);

   18     Console.WriteLine("정렬후.....");

   19     for (int i = 0; i < items.Length; i++)

   20         Console.WriteLine(items[i]);

   21 }

어떠신가요 잘 돌아가나요??아닙니다. 엄청난 에러가 발생하는데요 문제는 Sort라는 녀석 때문에 발생하게 됩니다. 이유는 Item 객체를 정렬할 수가 없기 때문에 나오는 에러입니다. 보통 sort는 문자, 문자열, 숫자 일때 가능합니다. 서로 다른 형식으로 되어있는 객체를 정렬하기에는 그 능력이 못 미치는거죠. 그래서 우리는 그 능력을 업그레이드 해줄 필요가 있습니다. 바로 IComparable 인터페이스를 재정의해주는 것이지요.
Item 클래스를 다음과 같이 수정해보겠습니다.

    1 class Item : IComparable

    2 {

    3     public int Number { get; set; }

    4     public string Name { get; set; }

    5 

    6     public override string ToString()

    7     {

    8         return string.Format("{0}, [{1}]", Number, Name);

    9     }

   10 

   11     public int CompareTo(object obj)

   12     {

   13         Item item = obj as Item;

   14         if (item != null)

   15         {

   16             return this.Name.CompareTo(item.Name);

   17         }

   18         else

   19         {

   20             throw new Exception("비교할 수 없는 형식입니다.");

   21         }

   22     }

   23 }

IComparable 인터페이스를 상속받아 거기에 있는 CompareTo메소드를 재정의 하는 것입니다. 위에서는 item의 이름으로 비교를 하여 이름순으로 정렬이 될것입니다. 숫자로 정렬하고 싶다면 Number로만 바꿔주면 될것입니다. 혹시 같인 번호 같은 이름을 가질 경우도 정의 해놓으면 두개의 비교식을 가지게 될 것입니다. 결과를 보면 다음과 같습니다.

아이템 이름으로 정확히 정렬되는것을 볼수 있지요. 객체 정렬시에는 CompareTo 메소드를 꼭 재정의 해줘야만 재되로 된 검색을 할 수 있습니다. 꼭 기억하시길 바랍니다.

네 간단히 인터페이스에 대해서 알아보았습니다. 저 자신도 100% 다 모르는걸 포스트 하다보니 내용도 비약하고 횡설수설하고 ㅜㅜ

그래도 조금이나마 도움이 되었으면 하네요~

감사합니다.

728x90
반응형

'.NET > C#' 카테고리의 다른 글

C# Event  (0) 2010.01.18
C# delegate  (0) 2010.01.14
FileAndDirectory  (0) 2010.01.12
C# File Input/Output  (1) 2009.12.24
C# Collection  (0) 2009.12.21
Class(5)  (0) 2009.12.18
형변환에 대해서...  (0) 2009.12.18
System.Object 클래스 상속  (3) 2009.12.18
Property의 get, set 에 대한 간단한 정리  (0) 2009.12.18
Class(4)  (0) 2009.12.17