interface를 이용한 구현은 상속과는 다른 코드의 유연성을 가진다.
이해를 돕기 위해서 상속과 함께 내용을 구성했다.
상속
부모 클래스가 가지는 기능을 자식 클래스에서 사용, 수정, 확장할 수 있다. 이것을 자식 클래스가 부모 클래스를 상속 받았다고 한다.using UnityEngine;public class ParentScript : MonoBehaviour{}public class Chil
fore4022.tistory.com
Interface 사용 방법
- interface의 이름은 interface의 기능을 나타내는 이름으로 지어야 한다.
- 이동 -> IMoveable 피해를 받는 -> IDamageReceiver
- interface를 상속 받은 class는 모든 메서드를 구현해 주어야 한다.
using UnityEngine; public class Player : MonoBehaviour, IMoveable { public void Move() { Debug.Log("Move"); } }using UnityEngine; public class Player : MonoBehaviour, IMoveable { }//IMoveable의 메서드 Move를 구현하지 않아서 오류가 발생한다. - public interface IMoveable { public void Move(); }
상속과 Interface의 차이점
상속
- 메서드 재정의를 통해서 구현하기 때문에, 구현의 일정 부분이 제한을 받는다.
- 상속으로 만든 각 클래스가 어떤 추가적인 기능을 가지고 있는지 알 수 없다.
- 같은 기능의 메서드를 구현하지 않아도 되기 때문에 생산성이 높다.
Interface
- interface만으로 어떤 기능을 가지는 class인지 구분할 수 있다.
- interface를 통한 class의 구현은 확장이 용이하다.
- interface를 통해 구현한 메서드는 interface를 통해서 접근할 수 있다.
상속을 이용한 구현과 Interface를 이용한 구현
우선, 상속을 이용한 구현을 살펴본 이후에, interface를 이용한 구현을 확인해 보겠다.
"요구한 몬스터들을 구현해주세요."
1번 몬스터 : "움직이지 않고, 제자리에서 공격하는 몬스터, 피해를 받음"
2번 몬스터 : "피해를 받지 않고, 플레이어를 쫓아다니기만 하는 몬스터, 공격하지 않음"
몬스터들이 가지는 정보 : 공격력, 체력, 이동속도
상속
부모 class에는 어떤 메서드가 필요할까?
"몬스터는 움직여야 하니까 Move(), 플레이어를 공격도 해야하니까 Attack(), 플레이어가 공격했을 때 피해를 받아야 하니까 TakeDamage()..."
아마도, 위의 글에 나온 메서드들을 포함한 부모 class를 만들 것이다. 그렇다면, 여기서 각 몬스터의 구현에 집중해 보겠다.
위의 몬스터들을 구현하는데 위의 부모 class를 사용하였을 때 아래와 같은 상황이 발생한다.
1번 몬스터 : "Move()메서드르 사용하지 않네?"
2번 몬스터 : "TakeDamage()메서드를 사용하지 않네?"
interface
몬스터가 가질 수 있는 기능들을 생각해보자. 어떤 interface들이 필요할까?
"몬스터는 움직여야 하니까 IMoveable, 플레이어를 공격도 해야하니까 IAttackable, 플레이어가 공격했을 때 피해를 받아야 하니까 IDamageReceiver..."
위의 interface들을 위해서 각 몬스터들을 구현해 보자.
1번 몬스터 : "IAttackable, IDamageReceiver 사용"
2번 몬스터 : "IMoveable 사용"
필요한 interface만을 이용하여서 구현할 수 있다.
상속과 interface
public void BaseMonster : MonoBehaviour
{
protected MonsterSO monsterSO;
}
public class ChildMonster1 : BaseMonster, IAttackable
{
public void Attack()
{
}
}
public class ChildMonster2 : BaseMonster, IMoveable
{
public void Move()
{
}
}
상속과 interface를 이용해서 보다 유연한 코드를 작성할 수 있을 것이다.
사용하지 않는 필드
위의 구현을 보았을 때, 아래와 같은 질문이 생길 것이다.
"1번 몬스터는 이동속도가 필요 없고, 2번 몬스터는 체력을 가지지 않아도 되지 않나?"
먼저 질문에 대한 답을 하자면 “가지지 않아도 됩니다.” 그렇다면 우리는 위의 상황에 대한 해결책을 생각해 볼 수 있을 것이다.
1번 해결책 : "각 몬스터 종류에 맞는 SO 생성"
2번 해결책 : "체력, 공격력, 이동속도... 생성"
3번 해결책 : "몬스터를 관리해주는 스크립트를 통해 값이 있는 필드만 몬스터에게 넘겨주기"
위의 예시로 사용한 해결책들 말고도, 다른 방법은 얼마든지 존재한다. 어떤 방법을 사용하는지는 개인이 결정할 사항이다.
하지만, ‘사용하지 않는 필드가 존재하는 경우를 해결해야 한다.’라고 생각할 필요는 없다. 우선 예시의 해결책들을 살펴보겠다.
1번 해결책 : "하나의 type으로 몬스터를 관리할 수 없음, 새로운 몬스터 종류가 생길 떄마다, 새로운 타입을 만들어야 한다."
2번 해결책 : "몬스터 종류에 맞춰서 관리하기에 불편함이 존재한다, 몬스터 하나가 가지는 정보의 개수에 따라서 생성할 타입의 양이 많아진다."
3번 해결책 : "값이 없는 필드에 불필요한 할당은 없어지지만, 정보를 넘겨주는 초기에 속도가 느려진다."
위의 해결책들 또한 단점이 존재한다. 사용하지 않는 필드를 문제 없이 깔끔히 없애는 방식은 없다.
interface가 익숙하지 않더라도, 가벼운 요소부터 적용 시키면서 사용에 익숙해질 수 있었으면 한다.
'유니티 > 개념 정리' 카테고리의 다른 글
| Predicate(술어) (0) | 2024.11.11 |
|---|---|
| Static(정적) (0) | 2024.11.04 |
| ObjectPool (0) | 2024.10.23 |
| 실행 결과 보장 (0) | 2024.10.21 |
| Non-MonoBehaviour (0) | 2024.10.14 |