티스토리 뷰

객체 지향의 꽃이라고도 할 수 있는 객체들 간의 '관계'는 설계에 있어서 중요하다고 할 수 있습니다. 객체 간의 관계를 정의하는 키(key)라고도 볼 수 있습니다.

 

이 포스팅에서 is-a 와 has-a에 대해 간단히 알아 본 다음, 어떠한 상황에서 사용해야 하는지에 대해 알아보도록 하겠습니다.

 

 

 

is-a


is-a는 말 그대로 'A는 B이다'일 때의 '~이다'와 같습니다. 좀 더 구체적인 얘기를 위해 위키피디아의 내용을 발췌하였습니다 :

is-a는 추상화(형식이나 클래스와 같은)들 사이의 포함 관계를 의미하며, 한 클래스 A가 다른 클래스 B의 서브클래스(파생클래스)임을 이야기합니다. 다른 말로, 타입 A는 타입 B의 명세(specification)를 암시한다는 점에서 타입 B의 서브타입이라고도 할 수 있습니다.


is-a 관계는 타입 또는 클래스 간의 has-a 관계와는 대조됩니다. has-a 및 is-a 관계들 간의 혼동은 실세계 관계 모델에 대한 설계에 있어 자주 발견되는 에러입니다. is-a 관계는 또한 객체 또는 타입 간의 instance-of 관계와도 대조됩니다.

 

학부에서 들었던 내용과 비슷할 것입니다. 굳이 코드(C++)로 구현하면 다음과 같은 예시가 될 수 있을 것입니다 :

// referenced by https://en.wikipedia.org/wiki/Is-a
class A
{ 
public:
   void DoSomethingALike() const {}
};

class B : public A
{ 
public:
   void DoSomethingBLike() const {}
};

void UseAnA(A const& some_A)
{
   some_A.DoSomethingALike();
}

void SomeFunc()
{
   B b;
   UseAnA(b); // b can be substituted for an A.
}

 

위 코드에서, UseAnA 함수에서 타입을 A로 받고 있지만 서브 클래스인 b가 인자로 들어가서 b의 DoSomethingBLike를 실행하게 될 것입니다.

 

 

 

has-a


is-a에 대해서도 알아봤으니 그와 반대인 has-a에 대해서도 알아봐야 겠지요. has-a는 위키피디아에서 다음과 같이 정의하고 있습니다 :

has-a는 구성 관계를 의미하며 한 오브젝트(구성된 객체, 또는 부분/멤버 객체라고도 부릅니다)가 다른 오브젝트(composite type이라고 부릅니다)에 "속한다(belongs to)"를 말합니다. 단순히 말해, has-a 관계는 객체의 멤버 필드라고 불리는 객체를 말하며, Multiple has-a 관계는 소유 계층구조를 형성하기 위해 결합하는 경우를 말합니다.

 

has-a 관계 역시 C++로 구현하면 다음과 같은 예시가 될 수 있을 것입니다 :

class A
{
public:
    void DoSomething()
    {
        b.Doing();
private:
    B b;
};

class B
{
public:
    void Doing()
    {
        // Do something in B class
        ...
    }
};

 

주목할 점은 A의 멤버 필드로 B를 가지고 있고, 이를 A의 기능 구현에 사용하고 있다는 점을 보시면 될 것 같습니다.

 

 

 

has-a vs is-a?


그렇다면 has-a와 is-a를 어떻게 구분하여 사용하는 지에 대해서 살펴보겠습니다. 둘의 정의를 살펴봤으므로, 둘의 특성에 대해서 알아보도록 하겠습니다 :

 

is-a 관계를 통해 생성된 클래스 및 객체는 상속 관계에서 둘은 밀접하게 결합되므로 부모 또는 기저 클래스의 명세에 변경이 발생하면 코드가 손상될 위험이 있습니다. 대신에 이러한 밀접한 관계는 클래스 계층구조에서 좀 더 안정적인 기반을 마련한다는 의미이기도 합니다. 게다가 상위 클래스의 기능을 하위 클래스가 물려받아 사용할 수 있는 장점도 있습니다.

 

반면 has-a 방식으로 생성된 클래스 및 객체는 느슨하게 결합됩니다. 이는 상속에 비해서 명세에 변경이 발생하더라도 구성 요소를 쉽게 변경할 수 있다는 의미입니다(코드의 손상이 적거나 없다는 의미입니다). 이러한 점에서 더 많은 유연성을 제공합니다. 하지만, has-a 방식은 상속보다 항상 낫다고 말할 정도로 단순한 문제가 아니며 실상은 더 복잡합니다.

 

 

어떻게 사용할 것인가

is-a 관계 생성과 has-a 관계 생성에 있어 마법의 은탄환이 있으면 정말 좋겠지만, 아쉽게도 공학에 그러한 것은 없습니다. 따라서 어떤 경우에 상속을 사용하고, 어떤 경우에 구성 관계를 선택하는지를 알아보겠습니다.

 

상속의 경우 is 관계가 확실하면 사용하는 것이 좋습니다. 몇 가지 예는 다음과 같습니다 :

  • 사람은 인간이다.
  • 고양이는 동물이다.
  • 게임 캐릭터는 엔티티이다.

 

이 경우, 상속을 사용하게 되면, 코드가 강한 결합을 이루게 되어, 설계를 더 단단하게 만들 수 있습니다. 또한, 상속이 사용되기 좋은 예는 다음 코드입니다 :

class Animal 
{

    virtual void emitSound() {
        printf("The animal emitted a sound");
    }

}

class Cat : public Animal 
{


    virtual void emitSound() 
    {
        printf("Meow");
    }
}

class Dog : public Animal 
{
}

void main() 
{
    Animal* cat = new Cat(); // Meow
    Animal* dog = new Dog(); // The animal emitted a sound
    Animal* animal = new Animal(); // The animal emitted a sound

    cat.emitSound();
    dog.emitSound();
    animal.emitSound();
    ...
}

 

위의 경우를 보면, Animal 클래스가 가상 함수를 선언함으로써 하위 클래스에서 다형성을 가질 수 있도록 하고 있습니다. 이것은 구성 관계에서는 할 수 없는 동작이며, 오로지 상속 관계에서만이 누릴 수 있는 특권입니다.

 

 

구성(composition)의 경우, 하나의 객체가 다른 객체를 "(부분으로써) 갖거나" 하는 경우에 사용할 수 있습니다. 몇 가지 예는 다음과 같습니다 :

  • 자동차는 배터리를 가지고 있다.
  • 사람은 심장을 가지고 있다.
  • 전투기는 HUD를 가지고 있다.

 

이러한 경우, has-a 관계를 통해 유연하게 구성할 수 있습니다. is-a 관계가 확실한지에 대해서 의문을 가지고 있다면, has-a 관계를 통해 구성하는 것이 방법이 될 수 있습니다.

 

 

 

레퍼런스


위키피디아 - Is-a
위키피디아 - Has-a
JAVAWORLD - Inheritance versus composition : How to choose
pacs tistory - [C++]상속 세번째, 상속의 조건 (is-a와 has-a 그리고 포함)
stackoverflow - What is the difference between IS -A relationship and HAS-A relationship in Java? [duplicate]

댓글