[C#] C#으로 공부하는 객체지향 프로그래밍 3 - 객체지향의 4가지 특성 : 상속




객체 지향은 4가지의 특성을 가지고 있다.

  1. 추상화 (Abstraction)
  2. 캡슐화 (Encapsulation)
  3. 상속 (Inheritance)
  4. 다형성 (Polymorphism)
이번엔 상속에 대해서 알아보자.






상속 (Inheritance) 이란?

부모 객체의 속성을 자식 객체가 부여 받는 것을 의미한다. 가령 아래와 같은 관계를 생각해보자.

생물 > 동물 > 인간

과학 시간은 아니니까 짧게 말하면 생물은 뭐 대충 '살아 있다' 라는 특성을 가진다고 하자. 동물은 생물의 범위에 포함되므로 역시 '살아 있다' 라는 속성을 가지고 있게 된다. 또한 동물은 식물과는 다르게 '먹는다', '잔다' 라는 특성이 존재한다. (물론 예외적인 경우는 제외하도록 하자.) 인간도 동물이기 때문에 위의 속성을 가지고 있으며, 인간은 '생각한다', '의사소통을 한다'라는 속성이 존재한다. 즉 아래와 같은 형태가 나온다.

생물 - 살아 있다 
동물 - 살아 있다, 먹는다, 잔다
인간 - 살아 있다, 먹는다, 잔다, 생각한다, 의사소통을 한다

즉, 생물의 직계 자식 객체인 동물은 생물의 속성을 물려 받으면서 자신의 속성을 가지고 있고, 인간은 생물과 동물의 자식 객체이므로 생물, 동물의 속성을 모두 가지면서 자신의 속성을 가지게 된다.

요약해서 상속 (Inheritance) 은 자식 클래스가 부모 클래스의 속성을 물려받는 것이다.






상속은 어떻게 사용할까?

Human 클래스를 다시 보자. Human 클래스는 아래와 같은 모양이었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Human : MonoBehaviour
{
    public string Hname { get; set; }
    public int Hage { get; set; }
    private int ID;

    public Human()
    {

    }

    public Human(string s, int i)
    {
        Hname = s;
        Hage = i;
    }
}



이 때, 우리는 Hacker 라는 클래스를 만들고 싶다고 하자. 이 Hacker는 당연하게도 인간의 속성을 가지게 된다. (상식적으로 생각해서 사람이니까!) Hacker 클래스는 아래와 같다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Hacker : MonoBehaviour
{
    public string Hname { get; set; }
    public int Hage { get; set; }
    public int HackingSkill { get; set; }

    private int ID;


    public Hacker()
    {

    }

    public Hacker(string s, int i)
    {
        Hname = s;
        Hage = i;
    }

    public Hacker(string s, int i, int Hack)
    {
        Hname = s;
        Hage = i;
        HackingSkill = Hack;
    }
}


자, 이제 뭔가 이런 생각이 들기 시작할 것이다.
'아니, 고작 HackingSkill 이란 속성 하나 다르다고 클래스를 새로 똑같이 다시 작성해야하나?'

그렇다고 Human 클래스에 그냥 HackingSkill을 넣을 수는 없는 노릇이다. 위는 자식 클래스가 Hacker 하나 밖에 없으니 넣을 만 하다고 생각할 수도 있지만, 인간은 여러 직업을 가지고 있다. 소방관, 의사, 마트 점원 등등... 심지어 그들이 가지고 있는 속성도 충분히 여러개이며, 특정 직업이기에 처리할 수 있는 경우가 있을 수도 있고, 다시 자세하게 설명하겠지만 Human 클래스 하나에 모든 걸 작성하기엔 너무 복잡하다. 그렇기 때문에 상속의 개념이 존재하는 것이다.

일단 두 클래스는 Monobehaviour라는 유니티의 최상위 클래스를 상속 받고 있는데, 여기서 Hacker는 Human 클래스의 직계 자손이라고 생각하자. 처음의 예시에 대입한다면

Monobehaviour <---> '생물'
Human <---> '동물'
Hacker <---> '인간'

과 같이 순서가 대응된다고 생각할 수 있다.




이제 Hacker 클래스에서

public class Hacker : Monobehaviour  -> public class Hacker : Human

로 상속 대상을 변경해보자. 이렇게 하고 Hacker 클래스를 아래와 같이 바꿔준다고 하자.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Hacker : Human
{
    public int HackingSkill { get; set; }

    public Hacker(string s, int i, int id, int Hack)
    {
        Hname = s;
        Hage = i;
        ID = id;
        HackingSkill = Hack;
    }
}



(Visual Studio 기준) 아마 ID = id 부분에 문제가 생길 것이다. 왜냐? ID 는 private 이기 때문이다.

캡슐화 부분에서 얘기 했듯이, private은 어떤 클래스 간에 접근을 불허한다. 따라서 외부 클래스의 접근은 불허하고 싶지만, 자식 클래스에겐 속성을 부여하고 싶으면 protected를 사용하면 된다. 


이제 Hacker는 Human의 속성을 가지고 있으며, HackingSkill 속성을 추가로 가지고 있게 되며, Human의 자식 클래스이므로 Human의 부모 클래스인 Monobehaviour 클래스의 속성 또한 가지게 된다.
다만 Hacker 클래스는 Human 클래스의 생성자를 사용할 수 없으므로, 다른 형태의 생성자가 필요하다면 추가 작성을 해줘야 한다. (물론 기본 생성자는 작성하지 않아도 될 것이다.)





그렇다면 상속은 왜 사용할까?

사실 상속을 사용하지 않아도 되기는 한다. 위에서 말했듯이 생성된 객체가 어떤 직업인지 정해주는 함수와 그 직업이 갖는 특성을 Human 클래스에 다 때려박아도 상관 없기는 하다. 예를 들어 이런 식이다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Human : MonoBehaviour
{
    public string Hname { get; set; }
    public int Hage { get; set; }
    public string job;

    private int HackingSkill;
    private int KillFireSkill;
    private int sellingSkill;
    private int DoctorSkill;

    private int ID;


    public Human()
    {

    }

    public Human(string s, int i, int id, string j)
    {
        Hname = s;
        Hage = i;
        ID = id;
        job = j;
    }

    public void SkillLevelUp()
    {
        if(job == "hacker")
        {
            HackingSkill++;
        }
        else if(job == "firefighter")
        {
            KillFireSkill++;
        }
        else if (job == "seller")
        {
            sellingSkill++;
        }
        else if (job == "doctor")
        {
            DoctorSkill++;
        }
    }
}



이게 대체 무슨 짓인가.. Skill을 Level up 하나 하는 것 만으로도 코드가 저렇게 길어지는데, 다른 속성까지 추가하게 되면 길어지는 건 둘째치고 너무 지나치게 복잡해지고 보기 힘들어진다.

따라서 목적은 크게 2가지로 나눌 수 있다.

  1. 같은 내용을 다시 코딩하는 것을 방지한다.
  2. 적절한 분류로 보다 깔끔한 코딩을 할 수 있다.

1번 내용은 이미 사용법에서 설명을 했었고, 2번 내용은 바로 위의 내용처럼, 하나의 클래스에 전부 작성하지 않고 적당한 분류를 해준다. 가령 Hacker, FireFighter, Seller, Doctor 클래스를 따로 만들어서 Human 클래스에 상속을 받게 만들고, 각각 추가 속성을 작성해서 필요한 클래스를 사용하면 된다.

상속은 객체 지향에서 아주 중요한 내용이며, 객체 지향의 4가지 특성 모두와 밀접한 연관을 갖고 있기 때문에 앞으로도 계속 나올 것이고, 좀 더 자세한 설명을 계속할 예정이니 일단 여기에선

서브 클래스가 부모 클래스의 속성을 이어받는 것 정도로 알고 넘어가면 되겠다.




댓글