오늘은 프로그램의 유연성과 확장성을 극대화하는 다형성(Polymorphism)과 인터페이스(Interface)에 대해 정리해 보겠습니다.
다형성은 하나의 객체가 여러 가지 형태를 가질 수 있는 능력을 의미합니다. 객체 지향 프로그래밍의 핵심 중 하나로, 부모 클래스 타입의 변수로 자식 클래스 객체를 참조할 수 있는 특징을 가집니다.
이를 통해 각기 다른 자식 객체들을 하나의 공통된 부모 타입으로 묶어서 관리할 수 있으며, 동일한 메시지를 보냈을 때 각 객체가 자신의 타입에 맞는 고유한 행동을 수행하게 할 수 있습니다. 코드의 중복을 줄이고 유지보수를 편리하게 만드는 기초가 됩니다.
인터페이스는 클래스가 반드시 구현해야 하는 기능을 정의한 일종의 계약(Contract)입니다. 클래스와 달리 실제 구현 내용은 담지 않고, 어떤 함수가 있어야 하는지만 선언합니다. C#에서는 관례적으로 이름 앞에 I를 붙여 인터페이스임을 나타냅니다.
인터페이스의 가장 큰 목적 중 하나는 결합도(Coupling)를 낮추는 것입니다. 객체 간의 의존성이 강하면(고결합) 코드 수정 시 연쇄적인 오류가 발생하기 쉽지만, 인터페이스를 통해 중간 통로를 만들면 서로의 내부 로직을 몰라도 소통할 수 있는 디커플링(Decoupling) 상태가 되어 확장이 매우 자유로워집니다.
또한 네임스페이스(Namespace)를 활용하면 관련된 클래스나 인터페이스를 하나의 그룹으로 묶어 관리할 수 있어 이름 충돌을 방지하고 구조를 체계화할 수 있습니다.
// Interface 정의 영역
<style>
namespace MyGame.Combat
{
public interface IDamageable
{
void TakeDamage(int amount);
}
}
</style>
// MonoBehaviour 구현 영역
<style>
using UnityEngine;
using MyGame.Combat;
public class Player : MonoBehaviour, IDamageable
{
public void TakeDamage(int amount)
{
Debug.Log("플레이어가 " + amount + "의 데미지를 입었습니다.");
}
}
public class Monster : MonoBehaviour, IDamageable
{
public void TakeDamage(int amount)
{
Debug.Log("몬스터가 " + amount + "의 데미지를 입고 분노합니다.");
}
}
</style>
다형성을 활용해 객체를 다룰 때, 해당 객체가 실제로 어떤 타입인지 확인하거나 안전하게 변환해야 할 때가 있습니다.
is 연산자: 객체가 특정 타입인지 확인하여 참(True) 또는 거짓(False)을 반환합니다.
as 연산자: 객체를 특정 타입으로 형변환을 시도합니다. 변환이 가능하면 해당 객체를 반환하고, 불가능하면 에러를 내는 대신 null을 반환합니다.
<style>
using UnityEngine;
using MyGame.Combat;
public class CombatManager : MonoBehaviour
{
void ProcessAttack(GameObject target)
{
// target이 IDamageable 인터페이스를 가지고 있는지 확인
if (target is IDamageable)
{
Debug.Log("공격 가능한 대상입니다.");
}
// target을 IDamageable로 안전하게 형변환 시도
IDamageable damageable = target as IDamageable;
// 형변환에 성공했을 때만 로직 실행 (방어 코드)
if (damageable != null)
{
damageable.TakeDamage(10);
}
else
{
Debug.Log("공격할 수 없는 대상입니다.");
}
}
}
</style>
요약 및 결론
오늘은 객체 지향의 다형성과 인터페이스를 배웠습니다.
인터페이스를 통해 객체 간의 결합도를 낮추는 디커플링 개념은 규모가 큰 게임 프로젝트일수록 설계의 완성도를 결정짓는 중요한 요소인만큼 인터페이스 사용의 중요성에 대해서 이해할 수 있었습니다.
또한 as와 is 연산자를 적절히 활용하여 이전 시간에 배웠던 방어 코드나 조건문을 더욱 다양하게 사용할 수 있게 된 시간이었습니다.