[CPP-04 ex02] 인터페이스(Interface) 클래스

바람직한 추상 클래스 == 인터페이스

0. 과제 소개

[ex02: This code is unclean. PURIFY IT!]

1) 클래스 생성

우주를 지키는 두 종류의 마린 유닛 클래스가 있고 이들을 묶는 스쿼드 클래스가 존재한다. 두 유닛 클래스와 스쿼드 클래스는 각각의 인터페이스 클래스에게 상속받는다.

1.1) ISpaceMarine 인터페이스 클래스

class ISpaceMarine
{
public:
    virtual ~ISpaceMarine() {}
    virtual ISpaceMarine* clone() const = 0;
    virtual void battleCry() const = 0;
    virtual void rangedAttack() const = 0;
        virtual void meleeAttack() const = 0;
};
  • -> TacticalMarine 파생 클래스

    • clone() : 현재 객체 복사본 반환, 생성 시 "Tactical Marine ready for battle"

    • battleCry() : "For the holy PLOT!"

    • rangedAttack() : " attacks with a bolter "

    • meleeAttack() : " attacks with a chainsword "

    • 소멸자: "Aaaragh ..." 출력

  • -> AssaultTerminator 파생 클래스

    • clone() : 현재 객체 복사본 반환, 생성 시: " teleports from space "

    • battleCry() : "This code is unclean. PURIFY IT!"

    • rangedAttack : " does nothing "

    • meleeAttack : " attacks with chainfists "

    • 소멸: "I’ll be back..."

1.2) ISquad 인터페이스 클래스

class ISquad
{
public:
      virtual ~ISquad() {}
      virtual int getCount() const = 0;
      virtual ISpaceMarine* getUnit(int) const = 0;
      virtual int push(ISpaceMarine*) = 0;
};
  • -> Squad 파생 클래스

    • getCount(): 현재 분대에 있는 유닛 수 반환.

    • getUnit(N): N 번째 포인터 반환. (0부터 시작, 범위 벗어난 인덱스 NULL 포인터)

    • push(XXX) : XXX 유닛을 스쿼드 끝에 추가, 동작 후 스쿼드 유닛 수 반환. (null유닛 or 이미 스쿼드에 있는 유닛 추가는 의미 없음)

    • 새로운 스쿼드 할당 시 이전 것은 반드시 삭제되어야함. 메모리 누출하지말고, 깊은 복사 할 것, 스쿼드가 파괴되면 내부 마린도 순서대로 파괴 (대입연산자 오버로딩에서는 이미 전에 객체가 존재하면 지우고 시작해야한다)

2) 과제 조건

  1. 여러 종류의 마린 객체를 생성한다. 두 종류의 마린은 생성자 메세지가 달라야 한다.

  2. Squad 객체 생성 후 push() 함수를 통해 스쿼드안에 마린을 넣는다. 이때,

    1. 깊은 복사를 통해 넣어야 한다. (대입연산자 오버로딩할때 참조자 값만 복사하는 것이 아닌 객체 자체를 복사)

    2. 동일한 이름의 마린이나 null 마린은 당연히 스쿼드 안에 들어갈 수 없다.

  3. squad 객체를 소멸할 때, 그 내부의 모든 마린 객체들까지 순서대로 함께 소멸된다.

3) 테스트 코드

int main()
{
    ISpaceMarine* bob = new TacticalMarine;
    ISpaceMarine* jim = new AssaultTerminator;

    ISquad* vlc = new Squad;
    vlc->push(bob);
    vlc->push(jim);
    for (int i = 0; i < vlc->getCount(); ++i)
    {
        ISpaceMarine* cur = vlc->getUnit(i);
        cur->battleCry();
        cur->rangedAttack();
        cur->meleeAttack();
    }
    delete vlc;

    return 0;
}
[output]

$> clang++ -W -Wall -Werror *.cpp

$> ./a.out | cat -e

Tactical Marine ready for battle!$
* teleports from space *$
For the holy PLOT!$
* attacks with a bolter *$
* attacks with a chainsword *$
This code is unclean. PURIFY IT!$
* does nothing *$
* attacks with chainfists *$
Aaargh...$
I'll be back...$

1. 인터페이스(Interface)

cpp에서의 인터페이스는 가상 소멸자순수 가상 함수만 선언되어 있는 클래스이다. interface class를 사용하는 이유는 여러이유가 있지만 그중 하나는 이 클래스를 보면서 이 클래스를 상속받는 클래스들은 뭐를 하겠구나 한눈에 알수 있게 된다.

class IClass
{
    public:
        virtual ~IClass();
        virtual void myFunc1() = 0;
        ...
};

순수 가상 함수만으로 구성되어 있기 때문에 당연히 스스로 인스턴스를 생성할 수 없다. 해당 인터페이스를 상속받은 자식에서 반드시 구현을 해야 한다.

추상 클래스와 크게 무엇이 다른가 싶지만, 바람직한 추상 클래스 == 인터페이스 라고 생각하면 될 것 같다. 불필요한 데이터를 상속하지 않을 수 있고, 다형성을 함수 오버로딩을 어느 특정 클래스에서만 빼먹는 실수를 방지할 수 있다.

인터페이스 클래스를 하나 만들어 놓고, 일반적인 클래스들을 다중 상속을 통해 사용하게 된다면 좋은 디자인 패턴을 가진 프로그램을 설계할 수 있을 것이다.

Last updated