디자인패턴, Observer 옵저버
Observer의 뜻
영어 사전으로 보면 다음의 뜻을 가진다.
영어사전
- 1
보는 사람, 목격자
According to observers, the plane exploded shortly after take-off.
목격자들에 의하면 그 비행기는 이륙 직후에 폭발했다. - 2
(회의·수업 등의) 참관인[옵서버]
A team of British officials were sent as observers to the conference.
영국 공무원들 한 팀이 그 총회에 참관인으로 파견되었다. - 3
관찰자, 관측자
a royal observer
왕실 입회인
[네이버 영어사전에서 발췌]
Observer 패턴
Observer 패턴은 관찰자, 참관인을 뜻하는 패턴으로 객체의 상태 변화를 관찰하는 관찰자를 두어 객체 사이에 일 대 다의 의존 관계를 정의해 두고, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지받고 자동으로 갱신될 수 있게 만든다. 의존성을 가지는게 주요 특징이어서 Dependent(종속자)라고도 하고, 메인에 서브가 달라붙는 형식이기 때문에 Publish-Subscribe(발행-구독)라고도 한다.
즉, 관찰자 A에 객체B, C, D가 등록되고, B에서 id정보가 바뀌면 A를 통해 C, D에서도 id정보가 자동으로 업데이트되도록 구현하는 것이 옵저버 패턴이다.
구현
c#을 이용해 핵심만 구현해 보았다.
using TEST_Observer.Models;
namespace TEST_Observer
{
public interface IObserver
{
void UpdateInfo(UserInfo userInfo);
}
public interface ISubject
{
void Attach(IObserver o);
void Detach(IObserver o);
void Notify(UserInfo userInfo);
}
}
[Observer 인터페이스]
using System.Collections.Generic;
using TEST_Observer.Models;
namespace TEST_Observer
{
public class Subject : ISubject
{
List<IObserver> observers = new List<IObserver>();
public void Notify(UserInfo userInfo)
{
foreach (IObserver o in observers)
{
o.UpdateInfo(userInfo);
}
}
public void Attach(IObserver o)
{
observers.Add(o);
}
public void Detach(IObserver o)
{
observers.Remove(o);
}
}
}
[관찰자 객체]
이 코드에서 핵심은 Observer가 수행할 인터페이스를 만든 후 관찰자 객체에서 해당 인터페이스를 사용하고, 옵저버에 등록 혹은 삭제, 알림 등 이벤트를 생성해주는 것이다.
C# Winform에서 구현 한 예제
IObserver, ISubject인터페이스는 위에 작성한 코드와 동일하다.
관찰자가 되는 메인이 되는 폼, Subject 클래스를 FormMain에서 다음과 같이 작성한다.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using TEST_Observer.Models;
namespace TEST_Observer
{
public partial class FormMain : Form, ISubject
{
List<IObserver> observers = new List<IObserver>();
public FormMain()
{
InitializeComponent();
}
public void Notify(UserInfo userInfo)
{
foreach (IObserver o in observers)
{
o.UpdateInfo(userInfo);
}
}
public void Attach(IObserver o)
{
observers.Add(o);
}
public void Detach(IObserver o)
{
observers.Remove(o);
}
private void buttonFormShow_Click(object sender, EventArgs e)
{
Form1 form1 = new Form1(this);
form1.Show();
Form2 form2 = new Form2(this);
form2.Show();
Form3 form3 = new Form3(this);
form3.Show();
}
}
}
관찰 대상이 되는 폼, Form1, Form2, Form3의 코드는 다음과 같이 작성한다.
using System.Windows.Forms;
using TEST_Observer.Models;
namespace TEST_Observer
{
public partial class Form1 : Form, IObserver
{
ISubject subject;
public Form1(ISubject _subject)
{
subject = _subject;
subject.Attach(this);
InitializeComponent();
}
public void UpdateInfo(UserInfo userInfo)
{
labelUserName.Text = userInfo.UserName;
}
private void buttonUpdate_Click(object sender, System.EventArgs e)
{
UserInfo userInfo = new UserInfo();
userInfo.UserName = textBoxUserName.Text;
subject.Notify(userInfo);
}
}
}
using System.Windows.Forms;
using TEST_Observer.Models;
namespace TEST_Observer
{
public partial class Form2 : Form, IObserver
{
ISubject subject;
public Form2(ISubject _subject)
{
subject = _subject;
subject.Attach(this);
InitializeComponent();
}
public void UpdateInfo(UserInfo userInfo)
{
labelUserName.Text = userInfo.UserName;
}
private void buttonUpdate_Click(object sender, System.EventArgs e)
{
UserInfo userInfo = new UserInfo();
userInfo.UserName = textBoxUserName.Text;
subject.Notify(userInfo);
}
}
}
using System.Windows.Forms;
using TEST_Observer.Models;
namespace TEST_Observer
{
public partial class Form3 : Form, IObserver
{
ISubject subject;
public Form3(ISubject _subject)
{
subject = _subject;
subject.Attach(this);
InitializeComponent();
}
public void UpdateInfo(UserInfo userInfo)
{
labelUserName.Text = userInfo.UserName;
}
}
}
Form1에서 UserName 텍스트박스에 값을 입력하고, 정보 수정 버튼을 클릭하면 Form1뿐만 아니라 Form2, Form3의 labelUserName도 똑같이 변경된다.
프로젝트 파일 첨부
참고 문헌
- GoF의 디자인패턴
참고 사이트
- 위키백과, 옵서버 패턴
- dofactory, C# Observer
- Refactoring.Guru, Observer in C#