디자인 패턴

[디자인 패턴] 옵저버 패턴(Observer Pattern) in Unity

Janny_ 2022. 10. 20. 18:13

옵저버 패턴(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