본문 바로가기

교육 노트/C# 강의

[C# 때려잡기] C# 강의 32. 추상클래스와 인터페이스

 


1. 추상클래스

weapon이놈은 사실 상속을 위해서만 만들어놓은 놈이고 

new weapon이나 Weapon w; 이런식으로 웨폰 객체를 만들일은 없다.

그런데도 불구하고 attack 함수를 구현할 필요는 없다.


그래서 내용을 지웠다.



namespace ConsoleApp4
{
class Weapon
{
public virtual void Attack()
{
}
}
class Knife : Weapon
{
public override void Attack()
{
Console.WriteLine("칼로 공격!");
}
}

class Program
{
static void Main(string[] args)
{
Weapon w = new Knife();
w.Attack();
}
}
}


이렇게 코딩을 하고 같이 개발하는 친구에게 나머지 무기 50개를 만들어달라고 하고 코드를 넘겨주었다

넘겨줄때

Weapon의 attack함수를 오버라이딩 해서 만들어~

라고 신신당부 하였다.


그런데 친구가 49번째 무기까지 잘 만들다가 50번째 무기에서 술먹고 들어와 attack함수를 오버라이딩 하지 않았다.

그런데 마침 해당 무기가 마지막에서나 얻을수 있는 최종 무기라 출시후에 보스몹을 잡으려던 사람들이 보스를 잡으려고 아무리 노력해봐도

공격이 되지않아 우리가 만든 게임을 전부 환불해주어야 했다 ㅜ


이런상황을 미연에 방지하기위하여

C 은 추상 함수를 제공한다.



추상 함수는 abstract 키워드를 함수 앞에 붙여사용하며, 해당 함수가 1개이상있으면 클래스도 abstract 키워드를 붙여 추상 클래스로 만들어주어야한다.


아무것도 구현하지 않은 상태를 말한다.

그런데 그러면  {}이랑 무슨 차이인가?


함수를 이용하는 이유는

개념적으로 상위 클래스로 존재하나
해당 클래스에서 구현할 필요는 없고
자식 클래스에서 구현이 반드시 필요한 경우


중요한 점은 반드시 필요하다는것이다.

반드시


만약 구현을 하지 않으면?





안만들면 못쓴다.




추상 함수를 가지고있는 클래스는 애초에 인스턴스화 (객체화) 가 불가능하다

그래서 이걸 클래스는 클래스인데 추상적이라 추상 클래스라 부르고

추상 함수를 오버라이딩 하지 않으면 해당 클래스도 추상클래스가 되는것이다.


따라서 애초에 인스턴스화 가 불가능하여 에러 뿜뿜을 하게 된다.


친구와 같은 오류가 미연에 방지되는것이다.




2. 인터페이스

추상클래스는 1개 이상의 추상함수로 인하여 인스턴스화 할수없는 클래스를 의미한다.

어찌됫건 클래스이기때문에 다중상속이 불가능한것은 당연하며,

인스턴스화 할수 없기때문에 자식에게 구현을 강제, 위임한다.


어찌보면 추상클래스에서는 실제 구현이 아닌 일종의 "시그니쳐" 가 적혀있는 셈이다.

인터페이스는 이런 "시그니쳐" 만 정의할수있는 키워드 이다.


namespace ConsoleApp4
{
interface Weapon
{
void Attack();
}
class Knife : Weapon
{
public void Attack()
{
}
}

class Program
{
static void Main(string[] args)
{
Weapon w = new Knife();
w.Attack();
}
}
}

인터페이스에 정의된 함수들은 어차피 시그니쳐기때문에 별도로 키워드를 붙일 필요가 없다.

인터페이스에 정의된 함수는 그냥 죄다 pubilc의 추상 함수들이라고 생각하면 된다.


인터페이스에는 함수, 프로퍼티 기타등등 포함이 가능한데 어찌됫건 죄다 시그니쳐 (일종의 선언만) 뿐이고

최다 상속받은 클래스에서 구현해주어야한다.


추상클래스는 추상함수뿐만아니라 별도의 그냥함수, 변수 를 포함할수있고, 추상함수도 private은 안되지만 (자식에서 구현 불가능) protected는 된다.

그러나 인터페이스는 무조건 public 의 추상함수들이다.


추상클래스가 더 상위 호환같은데.. 이런걸 왜 만들어 두었냐 하면..


이름그대로의 "인터페이스" 를 위해서이다.


인터페이스란 일반적으로 유저와 직접 만나서 내부를 쉽게 사용할수있게 이루어진것인데 (UI 처럼)

프로그래밍에서 인터페이스 키워드도 비슷하게 프로그래머에게 내부 동작을 신경쓰지말고 사용할수있는 인터페이스를 제공해준다.

Weapon이라는 인터페이스만 가지고있으면

이 Weapon을 상속받은 각종 클래스들은 당연히 Weapon의 인터페이스들을 구현했을것이다,


그러면 우리는 각각 어떻게 구현됬나 신경쓰지않고

Weapon을 상속받은 모든 클래스를 그냥 Weapon으로 사용하면된다.


또한 추상클래스와의 큰 차이점중에 하나는

인터페이스는 다중 상속이 가능하다.

어찌보면 당연한건데 어차피 구현은 없고 시그니쳐만 있으므로 다른 인터페이스와 곂쳐도 상관이 없다..




3. 기본 제공 인터페이스

C#에서는 이미 만들어진 인터페이스들이있다.


abstract class Weapon
{
protected int power;
public abstract void Attack();
}

class Knife : Weapon, ICloneable, IComparable
{
public override void Attack()
{
}

public object Clone()
{
return this;
}

public int CompareTo(object obj)
{

Knife k = obj as Knife;
if (k == null)
{
throw new ArgumentException();
}
if (this.power < k.power)
{
return -1;
}
if (this.power == k.power)
{
return 0;
}
return 1;
}
}

뭐 throw는 일단 넘어가고

여기서는 ICloneable,IComparable

이라는 인터페이스를 상속받았다.

ICloneable (클론가능)은 Clone라는 함수를 반드시 구현해야한다.

IComparable (비교가능) 은 CompareTo 라는 함수를 반드시 구현해야한다.


더많은 인터페이스들이 있는데

이렇게 많은 인터페이스를 다중상속하여 함수들을 구현할수있다.


ICloneable은 기존에는 복사생성자를 통하여 복사품을 만들었는데

C# 주로 값은 복사한 새로운 인스턴스를 만들때 ICloneable 이라는 Clone이라는 함수를 구현해서 사용한다.

딮카피가 필요하다면 이 Clone에서 구현 해주면 된다.


IComparable 은 비교되는 개체의 상대 순서를 나타내는 값을 반환하는 함수를 구현해주어야하는데 . 반환 값에는 다음과 같은 의미가 있다.

0보다 작음 이 인스턴스가 정렬 순서에서 obj 앞에 옵니다.
0 이 인스턴스가 정렬 순서에서 obj와 동일한 위치에 있습니다.
0보다 큼 이 인스턴스가 정렬 순서에서 obj 뒤에 옵니다.

이렇게 대소비교를 하는 IComparable 인터페이스의 CompareTo 함수를 구현하면


값을 비교해서 정렬해주는 함수를 구현할때 실제로 어떤 클래스가 오는지 상관하지않고

해당 함수는 IComparable 받고 대소비교를 ComparaTo로만 하면된다.

그럼 다형성에의하여 IComparable을 상속받은 클래스는 자동으로 IComparable 인터페이스로써 사용될것이다.


그리고 보면 알수있듯이

일반적으로 인터페이스명은 대문자 I 로시작하는 암묵적인 룰이있다.

728x90