목차
옵저버(Observer)
옵저버 디자인 패턴은 객체의 상태 변화를 관찰하는 관찰자들(Observer)에게 변화를 알리기 위해 사용하는 디자인 패턴이다. 어원적으로, '옵저버(Observer)'는 '관찰자'를 뜻하는 영단어로, 이 패턴에서는 한 객체의 상태 변화를 관찰하고, 그 변화에 반응하는 객체들을 의미한다.
이 패턴의 주요 구성 요소는 다음과 같다:
- Subject: 상태 변화가 발생하는 객체. Observer들을 자신의 리스트에 등록하고, 상태 변화가 있을 때 등록된 Observer들에게 알린다.
- Observer: Subject의 상태 변화를 관찰하고, 변화가 있을 때 업데이트 받아야 하는 객체들. Subject의 상태 변화에 따라 동작하는 메소드를 구현한다.
패턴 미적용 예시
패턴을 적용하지 않은 상황에서는 게임 내의 여러 UI 컴포넌트(예: 점수 표시, 생명력 표시 등)가 플레이어의 상태를 각자 독립적으로 확인해야 한다. 이 경우, 플레이어의 상태가 바뀔 때마다 각 UI 컴포넌트를 개별적으로 업데이트 해야 하는 번거로움이 있다.
class Player
{
public int Health { get; set; } = 100;
// 플레이어의 다른 속성들...
}
class HealthBar
{
public void Update(Player player)
{
Console.WriteLine($"HealthBar Updated: {player.Health}");
}
}
class ScoreDisplay
{
public void Update(Player player)
{
// 여기서는 예시를 단순화하기 위해 점수를 직접 Player에 넣지 않았음
Console.WriteLine($"Score Updated: {/* 플레이어의 점수 */}");
}
}
// 사용 예
var player = new Player();
var healthBar = new HealthBar();
var scoreDisplay = new ScoreDisplay();
// 플레이어의 상태가 변경될 때마다 수동으로 업데이트 호출
player.Health -= 10;
healthBar.Update(player);
scoreDisplay.Update(player);
이 코드에서 문제점은 플레이어의 상태가 바뀔 때마다 HealthBar와 ScoreDisplay를 수동으로 업데이트 해야 한다는 것이다. 또한, 새로운 UI 컴포넌트가 추가될 때마다 그 컴포넌트도 수동으로 업데이트해야 하는 부담이 생긴다.
패턴 적용 예시
옵저버 패턴을 적용한 경우, 게임에서 플레이어의 상태(예: 생명력) 변화를 관찰하는 UI 컴포넌트들이 플레이어의 상태 변화를 자동으로 받아 업데이트할 수 있다. 이를 통해 코드의 결합도를 낮추고, 유지보수성을 향상시킬 수 있다. 아래는 C#을 사용한 옵저버 패턴의 구현 예시이다.
// Observer 인터페이스 정의
interface IObserver
{
void Update();
}
// Subject 인터페이스 정의
interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
// Player 클래스가 Subject 역할을 한다
class Player : ISubject
{
private List<IObserver> observers = new List<IObserver>();
private int health = 100;
// Health 속성 변경 시 모든 Observer에게 알림
public int Health
{
get => health;
set
{
health = value;
Notify();
}
}
// Observer를 추가한다
public void Attach(IObserver observer)
{
observers.Add(observer);
}
// Observer를 제거한다
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
// 모든 Observer에게 상태 변경을 알린다
public void Notify()
{
foreach (var observer in observers)
{
observer.Update();
}
}
}
// HealthBar는 Observer의 구현체이다.
class HealthBar : IObserver
{
private Player player;
public HealthBar(Player player)
{
this.player = player;
}
public void Update()
{
Console.WriteLine($"HealthBar Updated: {player.Health}");
}
}
// ScoreDisplay는 Observer의 또 다른 구현체이다.
// 예제를 단순화하기 위해 실제로 점수를 관리하는 로직은 생략함
class ScoreDisplay : IObserver
{
public void Update()
{
Console.WriteLine("Score Updated: /* 플레이어의 점수 */");
}
}
// 사용 예
class Program
{
static void Main(string[] args)
{
var player = new Player();
var healthBar = new HealthBar(player);
var scoreDisplay = new ScoreDisplay();
// Observer들을 Player에 등록한다
player.Attach(healthBar);
player.Attach(scoreDisplay);
// 플레이어의 Health가 변경될 때마다, 등록된 Observer들이 자동으로 업데이트된다
player.Health -= 10;
// 이 라인 실행 시, HealthBar와 ScoreDisplay가 자동으로 업데이트됨
}
}
이 예시에서 Player 클래스는 ISubject 인터페이스를 구현하며, 게임 내의 주요 상태(예: 건강 상태)를 관리한다. HealthBar와 ScoreDisplay클래스는 IObserver 인터페이스를 구현하여 플레이어의 상태 변화를 감지하고 이에 대응한다. 플레이어의 건강 상태가 변할 때마다 Player클래스는 모든 등록된 관찰자(Observer)들에게 변경 사항을 알리며(Notify 메서드), 각 관찰자는 자신의 Update 메서드를 통해 필요한 UI 업데이트를 수행한다.
이 방식으로, 게임 개발자는 게임의 다양한 UI 요소들이 플레이어의 상태 변화에 따라 독립적으로, 그리고 자동적으로 업데이트되도록 할 수 있으며, 새로운 UI 요소를 추가하는 것도 훨씬 쉬워진다. 이는 코드의 재사용성과 확장성을 크게 향상시킨다.
'컴퓨터 과학 > 디자인패턴' 카테고리의 다른 글
[디자인패턴] 행위 패턴(9) : 상태(State) (1) | 2024.03.25 |
---|---|
[디자인패턴] 행위 패턴(8) : 발행-구독(Publisher-Subscriber) (1) | 2024.03.25 |
[디자인패턴] 행위 패턴(6) : 메멘토(Memento) (1) | 2024.03.25 |
[디자인패턴] 행위 패턴(5) : 중재자(Mediator) (0) | 2024.03.25 |
[디자인패턴] 행위 패턴(4) : 반복자(iterator) (2) | 2024.03.25 |