728x90
반응형

지난 포스트에서 객체 지향 언어의 4대 기본 원칙 중 갭슐화와 추상화에 대해 알아보았다.

객체 지향 언어의 4대 기본 원칙 :

  • 캡슐화 (Encapsulation)
  • 추상화 (Abstraction)
  • 상속 (Inheritance)
  • 다형성 (Polymorphism)

이번 포스트에서는 상속에 대해 알아보려고 한다. 상속은 기본적으로 확장의 개념이다. 확장이라고 하면 스타크래프트 게임 확장팩 "블루드워"가 나왔을 때가 생각난다(너무 옛날 이야기인가...). 확장팩에는 럭커와 메딕 처럼 새로 추가되는 유닛들이 있었고, 골리앗의 대공 사거리 업그레이드 처럼 기존 유닛의 업데이트도 있었다. 상속은 이렇게 확장이라는 개념을 통해서 코드의 재사용성을 높일 수 있도록 하는 것이다.

class StarcraftUnits {
    public Unit marine;
    public Unit firebat;
    public Unit zerggling;
    .
    .
    .
}

// 확장팩인 BloodWar 는 기존 Starcraft 유닛을 상속받음.
class BloodWarUnits : StarcraftUnits {
    public Unit medic; 
    public Unit lurker;
    .
    .
    .
}

위 예제에서 BloodWarUnit 클래스는 StarcraftUnit 클래스를 상속받음으로써 기존에 이미 있었던 마린, 파이어뱃, 저글링들을 다시 구현하지 않고도 기존 유닛들을 사용할 수 있다.

기존 클래스에 있던 메소드를 자식클래스에서 변경할 수도 있다. 이 때 부모클래스는 변경을 허용하는 메소드 앞에 virtual 또는 abstract 키워드를 가지고 있어야 하고, 자식클래스에서는 override 키워드를 이용하여 부모클래스로부터 가져온 메소드에 변경이 있음을 알려줘야한다.

public class Dog
{
    public virtual void Bark()
    {
        Console.WriteLine("멍멍!");
    }
}

public class Puppy : Dog
{
    public override void Bark()
    {
        Console.WriteLine("삑삑!");
    }
}

위 예제는 Puppy 클래스가 Dog 클래스를 상속받지만 Bark() 메소드를 그대로 사용하지 않고, 변경하여 사용하는 예이다.

virtualabstract의 차이는 구현부가 있느냐 없느냐의 차이이다.

  • virtual는 부모클래스에 구현부가 있음 : 자식클래스에서 굳이 override하지 않아도 되며, 이때는 부모클래스의 메소드를 그대로 사용할 수 있음.
  • abstract는 부모클래스에 구현부가 없음 : 자식클래스에서 반드시 override 해줘야하고, 부모클래스 이름 앞에 abstract 키워드를 붙여서 추상클래스임을 명시해줘야함.

위에서 봤던 강아지 예제를 abstract를 사용해서 만들어보자.

public abstract class Dog // 클래스 명 앞에 abstract가 붙어있음.
{
    public abstract void Bark();
    // 구현부가 올 수 없음. 
    // 구현할 경우 에러 발생.
    // { 
    //    Console.WriteLine("멍멍!");
    // }
}

public class Puppy : Dog
{
    // Bark() 메소드를 반드시 만들어야함. 
    // 없을 경우 에러 발생.
    public override void Bark() 
    {
        Console.WriteLine("삑삑!");
    }
}

나는 이런 의문이 들었다.
부모클래스의 변경을 전혀 원치 않을 경우 virtual이나 abstract키워드를 사용하지 않고 자식클래스에서 변경하는 방법이 없을까?

방법이 없는 것은 아니다. new키워드를 사용하여 부모클래스의 메소드를 가릴 수 있지만 아니지만 중요한 문제점을 수반한다. 아래 두가지 예제를 보고 실행결과를 비교해보자.

new 키워드를 이용한 방법:

class Program
{
    static void Main(string[] args)
    {
        Dog myDog = new Puppy();
        myDog.Bark();
        //실행결과 --> 멍멍!
    }
}

public class Dog
{
    public void Bark()
    { 
        Console.WriteLine("멍멍!");
    }
}

public class Puppy : Dog
{
    public new void Bark() // new 키워드를 이용해 부모 메소드를 가린다.
    {
        Console.WriteLine("삑삑!");
    }
}

virtual 키워드를 이용한 방법:

class Program
{
    static void Main(string[] args)
    {
        Dog myDog = new Puppy();
        myDog.Bark();
        //실행결과 --> 삑삑!
    }
}
public class Dog
{
    public virtual void Bark()
    {
        Console.WriteLine("멍멍!");
    }
}

public class Puppy : Dog
{
    public override void Bark()
    {
        Console.WriteLine("삑삑!");
    }
}

실행 결과가 왜 달라졌을까?
다음 포스트에서 다룰 다형성에서 자세히 들어다보자.

728x90
반응형

+ Recent posts