옵저버 패턴(Observer Pattern)
옵저버 패턴에는 상태를 가지고 있는 주체 객체와 상태의 변경을 알아야 하는 관찰 객체가 있습니다. 여기서 옵저버는 말 그래도 무언가를 감시하는 역할을 한다는 뜻으로 어떤 '이벤트'가 일어나는 것을 감시하며 객체의 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 각 옵저버에게 통지하고 미리 정의해둔 어떠한 동작을 수행하도록 하는 디자인 패턴을 의미합니다.
- Subject가 Observer에 대해서 아는 것은 Observer가 특정 인터페이스를 구현한다는 것 뿐입니다.
- Observer는 언제든지 새로 추가할 수 있습니다.
- 새로운 형식의 Observer를 추가해도 Subject를 변경할 필요 없이 새로운 클래스에서 Observer 인터페이스만 구현하면 됩니다..
- Subject나 Observer가 바뀌더라도 서로에게 영향을 주지 않습니다. 따라서 Subject와 Observer는 서로 독립적으로 재사용할 수 있습니다.
옵저버 패턴의 구현 예제
예제1
- Observer
// 옵저버 추상클래스
// : 옵저버들이 구현해야 할 인터페이스 메서드
public abstract class Observer
{
// 상태 update 메서드
public abstract void OnNotify();
}
- ConcreteObserver1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 옵저버 구현클래스
public class ConcreteObserver1 : Observer
{
// 대상타입의 클래스에서 이 메소드를 실행시킴
public override void OnNotify()
{
Debug.Log("옵저버 클래스의 메서드 실행 #1");
}
}
- ConcreteObserver2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 옵저버 구현클래스
public class ConcreteObserver2 : Observer
{
// 대상타입의 클래스에서 이 메소드를 실행시킴
public override void OnNotify()
{
Debug.Log("옵저버 클래스의 메서드 실행 #2");
}
}
- Subject
// 대상 인터페이스
// : 옵저버 관리, 활용에 관한 타입 정의
public interface ISubject
{
void AddObserver(Observer obs);
void RemoveObserver(Observer obs);
void Notify();
}
- ConcreteSubject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 대상 클래스
// : 대상 인터페이스를 구현한 클래스
public class ConcreteSubject : MonoBehaviour, ISubject
{
List<Observer> observers = new List<Observer>(); // 옵저버를 관리하는 List
// 관리할 옵저버를 등록
public void AddObserver(Observer observer)
{
observers.Add(observer);
}
// 관리중인 옵저버를 삭제
public void RemoveObserver(Observer observer)
{
if (observers.IndexOf(observer) > 0) observers.Remove(observer);
}
// 관리중인 옵저버에게 연락
public void Notify()
{
/*
for (int i = 0; i < observers.Count; i++)
{
observers[i].OnNotify();
}
*/
// for문 or foreach문 사용
foreach (Observer obs in observers)
{
obs.OnNotify();
}
}
void Start()
{
Observer obj1 = new ConcreteObserver1();
Observer obj2 = new ConcreteObserver2();
AddObserver(obj1);
AddObserver(obj2);
}
}
- 옵저버들을 리스트로 관리
- Notify() 함수를 통해 모든 옵저버의 OnNotify() 메서드를 실행
-> 유니티 버튼의 이벤트에 Notify() 메서드를 추가하면 버튼이 눌렸을 때 모든 옵저버의 OnNotify() 메서드 실행
예제2 : Subject로부터 Parameter 데이터를 받을 때
- Observer
// 옵저버 추상클래스
// : 옵저버들이 구현해야 할 인터페이스 메서드
public abstract class Observer
{
// 상태 update 메서드
public abstract void OnNotify(int num);
}
- ConcreteObserver1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 옵저버 구현클래스
public class ConcreteObserver1 : Observer
{
GameObject obj;
// 생성자를 통해 객체 전달
public ConcreteObserver1(GameObject obj)
{
this.obj = obj;
}
// 대상타입의 클래스에서 이 메소드를 실행시킴
public override void OnNotify(int num)
{
int num2 = obj.gameObject.GetComponent<ConcreteSubject>().getNum();
Debug.Log("옵저버 클래스의 메서드 실행 #1");
Debug.Log("메서드의 파라미터 : " + num);
Debug.Log("객체 변수를 통한 접근 : " + num2);
}
}
- ConcreteObserver2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 옵저버 구현클래스
public class ConcreteObserver2 : Observer
{
GameObject obj;
// 생성자를 통해 객체 전달
public ConcreteObserver2(GameObject obj)
{
this.obj = obj;
}
// 대상타입의 클래스에서 이 메소드를 실행시킴
public override void OnNotify(int num)
{
int num2 = obj.gameObject.GetComponent<ConcreteSubject>().getNum();
Debug.Log("옵저버 클래스의 메서드 실행 #2");
Debug.Log("메서드의 파라미터 : " + num);
Debug.Log("객체 변수를 통한 접근 : " + num2);
}
}
- Subject
// 대상 인터페이스
// : 옵저버 관리, 활용에 관한 타입 정의
public interface ISubject
{
void AddObserver(Observer o);
void RemoveObserver(Observer o);
void Notify();
}
- Concrete Subject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 대상 클래스
// : 대상 인터페이스를 구현한 클래스
public class ConcreteSubject : MonoBehaviour, ISubject
{
List<Observer> observers = new List<Observer>(); // 옵저버를 관리하는 List
private int myNum;
// 관리할 옵저버를 등록
public void AddObserver(Observer observer)
{
observers.Add(observer);
}
// 관리중인 옵저버를 삭제
public void RemoveObserver(Observer observer)
{
if (observers.IndexOf(observer) > 0) observers.Remove(observer);
}
// 관리중인 옵저버에게 연락
public void Notify()
{
foreach (Observer obs in observers)
{
obs.OnNotify(myNum);
}
}
void Start()
{
myNum = 10;
Observer obj1 = new ConcreteObserver1(this.gameObject);
Observer obj2 = new ConcreteObserver2(this.gameObject);
AddObserver(obj1);
AddObserver(obj2);
}
public int getNum()
{
return myNum;
}
}
- 옵저버들에게 myNum 데이터를 알려주고, 자신의 오브젝트(this.gameObject)를 넘겨 ConcreteSubject의 getNum() 메서드를 사용할 수 있게 해준다.
예제3 : delegate 사용
ConcreteSubject를 제외한 나머지 코드는 예제2와 동일
- ConcreteSubject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ConcreteSubject : MonoBehaviour
{
// List<Observer> observers = new List<Observer>(); // 옵저버를 관리하는 List
private int myNum;
delegate void NotiHandler(int num);
NotiHandler _notiHandler;
public void Notify()
{
_notiHandler(myNum);
}
void Start()
{
myNum = 10;
Observer obj1 = new ConcreteObserver1(this.gameObject);
Observer obj2 = new ConcreteObserver2(this.gameObject);
_notiHandler += new NotiHandler(obj1.OnNotify);
_notiHandler += new NotiHandler(obj2.OnNotify);
}
public int getNum()
{
return myNum;
}
}
- int 파라미터를 받는 delegate 객체를 생성하여 delegate에 옵저버의 OnNotify 메서드를 등록하여 실행시킨다.
'디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 싱글톤 패턴 (0) | 2022.10.19 |
---|