[TIL] C# virtual, override, 인터페이스
인터페이스 VS 추상화 클래스
유니티를 통해 게임 개발을 하던 와중, 문득 인터페이스와 추상화 클래스의 기능이 거의 비슷하지 않나 라는 생각이 들어, 이에 대해 자세히 공부하게 되었다.
정확히는 게임 내 살아있는(체력이 존재하며, 데미지를 입을 수 있는) 오브젝트를 추상화하는 스크립트를 구현하면서, 인터페이스 혹은 추상화 클래스 둘 중 어떤 방식을 선택할까 고민하던 도중 해당 의문을 가지게 되었다.
둘의 차이는 정리해보자면 다음과 같다.
| 구분 | 인터페이스 (Interface) | 추상 클래스 (Abstract Class) |
|---|---|---|
| 정의 | 클래스가 따라야 할 규칙(메서드 시그니처)만 정의 | 공통된 기능을 가진 클래스들의 기본 형태 정의 |
| 멤버 구성 | 오직 추상 메서드와 상수만 포함 (C#에서는 기본 구현도 가능) | 일반 메서드, 추상 메서드, 필드, 생성자 모두 포함 가능 |
| 다중 상속 | 다중 구현 가능 (class A : IInterface1, IInterface2) |
단일 상속만 가능 (class A : BaseClass) |
| 생성자 | 없음 | 있음 |
| 접근 제한자 | 기본적으로 public (C#에서는 명시적 인터페이스 구현 가능) | 접근 제한자 사용 가능 (private, protected, public 등) |
| 사용 목적 | 특정 동작을 보장하는 “계약”을 정의 | 공통 기능을 제공하면서도 일부 구현을 강제 |
즉 인터페이스는 다중 구현이 가능하므로 확장성이 뛰어나지만, 메서드의 내용을 정의할 수 없으므로 클래스의 행동을 규정하는데 적합하다.
추상 클래스는 데이터와 공통 기능을 제공하므로 일부 기능을 강제하는데 유용하지만, C#은 다중 상속이 불가능하므로 확장성이 떨어진다는 단점이 존재한다.
추상 클래스의 인스턴스 생성?
추상 클래스의 정의를 생각해보면, 사실 불가능한게 당연하다. 추상 클래스는 애초에 미완성된 클래스이므로, 상속을 통해 완성되는 클래스이기 때문이다. 만약 추상 클래스의 인스턴스를 생성한다면, 구현되지 않은 abstract 메서드가 존재할 수 있기 때문에 동작이 정의되지 않은 메서드가 호출될 가능성이 존재하게 된다.
virtual 키워드
virtual 키워드를 사용하면, 자식 클래스에서 override 를 통해 해당 메서드나 속성을 변경할 수 있다.
일반적인 오버라이딩과의 차이점은 다형성의 적용에 있다. virtual을 사용하여 오버라이딩을 하면 부모 메서드의 내용을 활용하고, 확장하는 의미를 가지지만 그냥 오버라이딩을 하게되면 부모 메서드와 완전히 별개로 동작하게 된다.
무엇보다 부모 타입으로 참조할 때 인스턴스가 자식 타입이더라도 virtual을 사용하면 자식 메서드를 실행하지만, 그냥 오버라이딩을 할 경우 부모 메서드가 실행된다.
따라서 virtual 키워드를 활용하면 다형성이 적용되므로, 상속 구조에서 유연하게 동작할 수 있다.
public class LivingEntity : MonoBehaviour, IDamageable
{
public float fullHP { get; protected set; } //최대 체력
public float currentHP { get; protected set; } //현재 체력
public bool isDead { get; protected set; } //사망 여부
public event Action OnDeath; //사망시 실행되는 이벤트
public virtual void OnDamage(float damage) //피격 시 호출되는 메서드
{
fullHP -= damage; //데미지 처리
if (fullHP <= 0)
{
Die(); //사망 처리
}
}
public virtual void Die()
{
OnDeath?.Invoke(); //사망 이벤트가 존재할 경우, 실행
isDead = true;
}
}
public class PlayerHealth : LivingEntity
{
private Animator anim;
void Start()
{
anim = GetComponent<Animator>();
}
public override void OnDamage(float damage)
{
base.OnDamage(damage); //원본 메서드 실행
anim.SetBool("isDamaged", true); //애니메이션 컨트롤
}
}
위처럼 virtual 메서드를 정의하면 자식 메서드에서 원하는 기능을 자유롭게 확장할 수 있다.
virtual , abstract 비교
| 구분 | virtual |
abstract |
|---|---|---|
| 기본 구현 제공 여부 | 가능 | 불가능, 반드시 하위 클래스에서 구현해야 함 |
| 재정의(Override) 가능 여부 | override 가능 |
override 필수 |
| 인스턴스 생성 가능 여부 | 단독 사용 가능 | 추상 클래스에서만 사용 가능 |
오늘 배운 핵심 정리
- 행동 규정의 인터페이스, 기능 강제의 추상 클래스
- virtual 키워드를 통해 다형성이 적용되는 오버라이딩 가능
- 추상 클래스는 직접 인스턴스를 생성할 수 없고, 반드시 상속 후 사용해야 한다.
댓글남기기