로직을 기능별로 모듈화시켜 조합하여 사용하는 것.
도서 '게임 프로그래밍 패턴'에서는 디커플링 패턴으로 분류하고 있다.
유니티는 이미 오브젝트에 컴포넌트를 붙이면서 행동을 추가하는 컴포넌트 패턴 방식을 사용하고 있다.
게임을 만들 때에, 플레이어는 이동하고, 점프하며, 공격할 수도 있고 벽에 부딪히기도 하는 여러 행동을 가지게 된다.
하지만 이러한 행동들을 하나의 클래스에서 정의하게되면 거대 클래스가 만들어진다.
클래스가 거대화 되면서 내부적으로 기능들의 커플링이 심각해지게 된다.
클래스가 얽히면서 수정사항이 다른 기능에 영향을 미치게되니 협업도 불가능해진다.
컴포넌트 패턴을 사용하면 클래스를 행동에 따라 여러개로 나눌 수 있다.
세부 기능들을 나눈 다음, 서로 통신이 필요한 경우만 결합하여 사용할 수 있다.
# 단순하게 짜본 컴포넌트 패턴
- 물론 이렇게 짜면 거의 하드코딩에 가깝다.
public class Player : Monobehaviour
{
public InputComponent input;
public PhysicsComponent physics;
public RenderComponent render;
private void Update()
{
input.Update(this);
physics.Update(this);
render.Update(this);
}
}
# Action을 활용해서 짜본 컴포넌트 패턴
- 컴포넌트 쪽에서 참조를 해오는 방식.
- 여기서 더 디커플링을 하려면 직접 Player를 참조하지말고 인터페이스를 느슨한 참조를 하면 된다.
public class Player : Monobehaviour
{
public Action<Player> updateAction;
private void Update()
{
updateAction?.Invoke(this)
}
}
public class InputComponent : Monobehaviour
{
private void Awake()
{
GetComponenet<Player>().updateAction += SubUpdate;
}
private void SubUpdate()
{
// 인풋관련
}
}
컴포넌트 패턴을 사용하면..
1. 단일 상속 문제를 해결할 수 있다.
다중 상속을 지원하지 않는 언어에서, 컴포넌트 패턴을 통해서 기능을 붙여나가면 다중 상속의 효과를 낼 수 있다.
2. 코드의 재활용이 가능하다.
특정 기능만을 분리해서 작성하기 때문에 범용적으로 작성해둔다면, 여러 클래스에서 사용이 가능하기 때문에 코드 길이를 절약할 수 있다.
컴포넌트들은 어떻게 통신하는가?
1. 컨테이너 객체의 상태를 변경하는 방식
- 컴포넌트는 컨테이너의 정보들을 업데이트하고, 컨테이너의 정보를 기준으로 행동한다.
1) 컴포넌트는 서로 디커플링 된다.
2) 컴포넌트들이 공유하는 정보를 컨테이너 객체에 전부 넣어야 한다.
- 클래스가 지저분해지고, 사용하지 않아도 넣어야되니 메모리 낭비할 수 있다.
3) 컴포넌트끼리 암시적으로 통신하다 보니 컴포넌트 실행 순서에 의존하게 된다.
- 인풋과 렌더링 순서가 바뀐다거나 하는 버그가 빈번하게 발생한다.
2. 컴포넌트가 서로 통신하는 방식
- 직접 참조를 한다.
1) 간단하고 빠르다.
2) 커플링이 강해진다.
- 그래도 일부 컴포넌트 간의 커플링이라 일반 클래스에서 통으로 결합된 것보다는 낫다.
3. 메시지로 통신하는 방식
- 컴포넌트가 컨테이너에 메시지를 보내면 컨테이너는 모든 컴포넌트에게 메시지를 전파한다.
1) 컴포넌트들이 디커플링된다.
2) 컨테이너 객체는 단순하다.
'🛡️ 디자인 패턴' 카테고리의 다른 글
타입 객체 패턴 (Type Object Pattern) (1) | 2021.09.22 |
---|---|
이벤트 큐 패턴 (Event Queue Pattern) (0) | 2021.08.22 |
커맨드 패턴 (Command Pattern) (0) | 2021.07.11 |