Quick Comparison
interface는 unrelated type도 구현할 수 있는 capability contract이고, abstract class는 derived type이 공유하는 state·constructor·non-public helper·template flow를 담는 base입니다. C# class는 하나의 base class만 가지므로 MonoBehaviour를 상속한 Unity component에는 다른 abstract base를 더 붙일 수 없습니다.
| 필요 | 먼저 선택 | Unity에서 흔한 형태 |
|---|---|---|
| caller가 behavior만 요구 | interface | IDamageable, ITargetable |
| shared field와 default flow | abstract class | plain C# state/ability base |
| MonoBehaviour 사이 공통 logic | component composition | Health, Targeting component |
| 여러 capability를 함께 제공 | 여러 interface | component가 필요한 contract 구현 |
public abstract class Ability
{
public bool TryUse(Actor actor) => CanUse(actor) && UseCore(actor);
protected abstract bool CanUse(Actor actor);
protected abstract bool UseCore(Actor actor);
}
public interface ITargetable { bool IsTargetable { get; } }상태와 계약을 분리하기
interface는 instance field를 선언하지 않는 behavior contract입니다. common HP, cooldown, validation flow처럼 shared state와 invariant가 있으면 abstract base 또는 separate component가 중복을 줄일 수 있습니다. 반대로 shared code가 거의 없고 caller가 “damage를 받을 수 있는가”만 알아야 한다면 base hierarchy 대신 interface가 적합합니다.
abstract base가 있다고 모든 subtype이 같은 lifecycle을 가져야 하는 것은 아닙니다. derived type이 template method의 precondition·result·cleanup을 지킬 수 있는지 test합니다. subtype마다 NotSupportedException이나 type check가 늘면 base contract가 넓거나 composition이 맞는 신호입니다.
Unity의 상속 경계
Unity Component는 보통 MonoBehaviour를 base로 하므로 gameplay의 reusable core를 plain C# class·ScriptableObject definition·separate Component 중 하나로 분리하는 선택이 많습니다. IDamageable interface를 구현한 MonoBehaviour가 Health component에 위임하면 caller contract와 shared state 구현을 함께 유지할 수 있습니다.
선택을 검증하기
새 abstraction은 최소 두 구현과 하나 이상의 consumer test가 생길 때 의미가 드러납니다. interface가 concrete cast를 요구하지 않는지, abstract base가 unrelated feature를 묶지 않는지, Inspector reference·scene lifecycle과 충돌하지 않는지 확인합니다.
“interface가 더 유연하다” 또는 “abstract class가 재사용된다”만으로 고르지 마세요. caller가 필요한 contract와 실제로 공유되는 state·lifecycle이 무엇인지가 선택 기준입니다.
참고 링크
2 sources