오늘은 객체 지향의 상속(Inheritance)과 다양한 재정의 기법들에 대해 정리해 보겠습니다.
상속은 (부모 클래스가 가지고 있는 멤버와 기능을 자식 클래스가 그대로 물려받는 것을 의미합니다. 이를 통해 코드의 재사용성을 높이고 클래스 간의 계층 구조를 형성할 수 있습니다.
부모 클래스를 기반으로 자식 클래스에서 기능을 추가하거나 변경할 수 있어 효율적인 개발이 가능합니다.
// 부모 클래스 영역
<style>
using UnityEngine;
public class Unit : MonoBehaviour
{
public string unitName;
public int hp;
public void Move()
{
Debug.Log(unitName + "이(가) 이동합니다.");
}
}
</style>
// 자식 클래스 영역
<style>
using UnityEngine;
public class Soldier : Unit // ':' 뒤에 부모클래스의 이름 정의를 통해 상속
{
public void Attack()
{
Debug.Log(unitName + " 소총수가 공격합니다!");
}
}
</style>
부모 클래스에서 물려준 기능을 자식 클래스에서 그대로 쓰지 않고, 자신만의 방식으로 바꾸어 정의해야 할 때가 있습니다. 이때 가상화와 재정의를 사용합니다.
이를 활용하면 부모 타입의 변수로 자식 객체를 다루더라도, 실제 실행될 때는 자식 클래스에서 재정의된 내용이 실행되어 객체마다 고유한 동작을 수행할 수 있습니다.
public class Monster : MonoBehaviour
{
public virtual void Roar()
{
Debug.Log("몬스터가 울음소리를 냅니다.");
}
}
public class Slime : Monster
{
public override void Roar()
{
Debug.Log("슬라임이 끈적하게 울어댑니다.");
}
}
자식 클래스에서 부모 클래스의 기능을 완전히 갈아치우는 것이 아니라 부모의 원래 기능도 사용하고 싶다면 base를 사용합니다.
오버라이드된 함수 안에서 base.부모함수이름()을 호출하면, 부모가 가진 기본 로직을 그대로 실행한 후 자식만의 추가 로직을 실행하는 식으로 동작합니다. 이는 부모의 공통 로직을 유지하면서 기능을 확장할 때 매우 유용합니다.
<style>
public class Slime : Monster
{
public override void Roar()
{
// 1. 부모 클래스(Monster)의 Roar 기능을 먼저 실행
base.Roar();
// 2. 그 다음 슬라임만의 고유한 로직 추가 수행
Debug.Log("슬라임이 점액을 뿜어내며 위협합니다.");
}
}
</style>
무조건적인 상속은 문제를 일으킬 수 있습니다. 부모 클래스가 너무 무거워지거나, 상속 관계가 복잡해지면 코드의 유지보수성이 떨어집니다.
따라서 상속은 클래스 간의 명확한 'IS-A(부모는 자식이다)' 관계일 때만 사용하고, 특정 기능을 추가하는 'CAN-DO(할 수 있다)' 관계는 인터페이스로 분리하는 것이 좋습니다. 상속으로 공통 기능을 묶고, 인터페이스로 필요한 능력만 골라 부여하는 적절한 조합이 가장 이상적인 설계 방식입니다.
오버로드는 이름은 같지만 매개변수의 타입이나 개수가 다른 함수를 여러 개 만드는 것을 의미합니다. 프로퍼티나 재정의(Override)와는 또 다른 개념으로, 동일한 이름의 함수가 다양한 형태의 입력값에 대응할 수 있게 해줍니다.
호출할 때 전달하는 데이터에 따라 컴파일러가 알아서 알맞은 버전의 함수를 실행해 주므로, 개발자는 함수 이름을 일일이 다르게 지을 필요가 없어 코드가 깔끔해집니다.
C#
public class Calculator : MonoBehaviour
{
// 정수 두 개 더하기
public int Sum(int a, int b)
{
return a + b;
}
// 실수 두 개 더하기 (오버로드)
public float Sum(float a, float b)
{
return a + b;
}
// 정수 세 개 더하기 (오버로드)
public int Sum(int a, int b, int c)
{
return a + b + c;
}
}
오늘은 클래스의 기능을 물려받는 상속과 재정의 기법들을 배웠습니다.
상속을 하였을 때 기능을 물려받아 새롭게 로직을 구성할 수 있는건 알았지만 base를 이용하여 부모가 구현한 로직을 사용할 수 있다는 점을 새롭게 알게 되는 시간이었습니다.
또한 오버로드를 통해 같은 이름의 함수를 상황에 맞게 변주하여 사용하는 법을 익히며 코드의 가독성을 높이는 습관을 들여야겠다고 생각했습니다.