7. Observer Pattern ( 옵져버 패턴 C# )
Unity3D 2014. 3. 21. 11:52
[읽기전에]
UML 다이어그램이 이해가 가지 않으시면 다음의 포스팅을 보고오세요 : UML클래스 다이어그램 기본상식 http://hongjinhyeon.tistory.com/25
포스팅되는 디자인 패턴의 예는 스타크래프트를 기본으로 하였습니다 : 디자인 패턴을 시작하며 http://hongjinhyeon.tistory.com/24
<기본 정의 >
1. Observer Pattern 정의
-한 객체의 상태가 바뀌면 그 책체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로
일대다(one-to-many) 의존성을 정의한다.
(Define a one-to-many dependency between objects so that when one object changes state,
all its dependents are notified and updated automatically.)
-한 객체의 상태가 변경되면 그 객체에 의존하는 모든 객체에 연락을 한다.
2. UML Diagram
3. 사용용도 및 장점
-옵져버 패턴은 MVC 모델에서 View단( 사용자에게 보여주는 단계)에서 많이 나타날수가 있다, 객체의 상태에대한
참조를 여러군데에서 하고 있을시에 ( 특히 여러개의 창에서 참조가 될시에 ) 사용될 수 있다.
-객체의 상태를 참조하는 대상에 일관성을 보장한다.
-객체의 상태가 변경될 시에 참조를 하고있는 대상들은 자동으로 상태가 업데이트가 된다.
4. 소스
001.
using
System;
002.
using
System.Collections.Generic;
003.
004.
namespace
DoFactory.GangOfFour.Observer.Structural
005.
{
006.
/// <summary>
007.
/// MainApp startup class for Structural
008.
/// Observer Design Pattern.
009.
/// </summary>
010.
class
MainApp
011.
{
012.
/// <summary>
013.
/// Entry point into console application.
014.
/// </summary>
015.
static
void
Main()
016.
{
017.
// Configure Observer pattern
018.
ConcreteSubject s =
new
ConcreteSubject();
019.
020.
s.Attach(
new
ConcreteObserver(s,
"X"
));
021.
s.Attach(
new
ConcreteObserver(s,
"Y"
));
022.
s.Attach(
new
ConcreteObserver(s,
"Z"
));
023.
024.
// Change subject and notify observers
025.
s.SubjectState =
"ABC"
;
026.
s.Notify();
027.
028.
// Wait for user
029.
Console.ReadKey();
030.
}
031.
}
032.
033.
/// <summary>
034.
/// The 'Subject' abstract class
035.
/// </summary>
036.
abstract
class
Subject
037.
{
038.
private
List<Observer> _observers =
new
List<Observer>();
039.
040.
public
void
Attach(Observer observer)
041.
{
042.
_observers.Add(observer);
043.
}
044.
045.
public
void
Detach(Observer observer)
046.
{
047.
_observers.Remove(observer);
048.
}
049.
050.
public
void
Notify()
051.
{
052.
foreach
(Observer o
in
_observers)
053.
{
054.
o.Update();
055.
}
056.
}
057.
}
058.
059.
/// <summary>
060.
/// The 'ConcreteSubject' class
061.
/// </summary>
062.
class
ConcreteSubject : Subject
063.
{
064.
private
string
_subjectState;
065.
066.
// Gets or sets subject state
067.
public
string
SubjectState
068.
{
069.
get
{
return
_subjectState; }
070.
set
{ _subjectState = value; }
071.
}
072.
}
073.
074.
/// <summary>
075.
/// The 'Observer' abstract class
076.
/// </summary>
077.
abstract
class
Observer
078.
{
079.
public
abstract
void
Update();
080.
}
081.
082.
/// <summary>
083.
/// The 'ConcreteObserver' class
084.
/// </summary>
085.
class
ConcreteObserver : Observer
086.
{
087.
private
string
_name;
088.
private
string
_observerState;
089.
private
ConcreteSubject _subject;
090.
091.
// Constructor
092.
public
ConcreteObserver(
093.
ConcreteSubject subject,
string
name)
094.
{
095.
this
._subject = subject;
096.
this
._name = name;
097.
}
098.
099.
public
override
void
Update()
100.
{
101.
_observerState = _subject.SubjectState;
102.
Console.WriteLine(
"Observer {0}'s new state is {1}"
,
103.
_name, _observerState);
104.
}
105.
106.
// Gets or sets subject
107.
public
ConcreteSubject Subject
108.
{
109.
get
{
return
_subject; }
110.
set
{ _subject = value; }
111.
}
112.
}
113.
}
5. 실행결과
Observer X's new state is ABC
Observer Y's new state is ABC
Observer Z's new state is ABC
< 실제 적용 >
1. UML Diagram
2. 사용용도 및 장점
-옵저버의 패턴은 스타크래프트에서 필수적인 요소입니다. 사용되는 곳은 여러곳이 있는데 여기서 예를 드는것은 유닛의 상태입니다.
마린의 체력 상태를 모니터링하는 곳은 메인화면( 케릭터의 에너지바가 칸수로 나옴 ), 상태창(캐릭터의상태가 숫자 표시 및 색으로 표현),
적의 화면 ( 상대편의 화면 및 케릭터 상태창에서의 체력 표시 )이 있습니다.
-아군 마린이 적의 마린의 공격으로 체력이 깍였다면 위의 3군데 모두 동일하게 남은 체력이 보여야 할 것입니다. 이럴때에 옵져버 패턴을
사용하면 동일한 시점에 동일한 상태를 나타내게 됩니다.
3. 소스
001.
using
System;
002.
using
System.Collections.Generic;
003.
using
System.Linq;
004.
using
System.Text;
005.
006.
namespace
Observer
007.
{
008.
class
Program
009.
{
010.
static
void
Main(
string
[] args)
011.
{
012.
013.
Marine ourMarine =
new
Marine(
"아군 마린"
, 100);
014.
ourMarine.Attach(
new
MainScreen());
015.
ourMarine.Attach(
new
StatusScreen());
016.
ourMarine.Attach(
new
EnemyScreen());
017.
018.
ourMarine.Health = 60;
019.
ourMarine.Health = 40;
020.
021.
Console.ReadKey();
022.
}
023.
024.
abstract
class
Unit
025.
{
026.
private
string
name;
027.
private
int
health;
028.
private
List<UnitViewer> unitViewers =
new
List<UnitViewer>();
029.
030.
public
Unit(
string
name,
int
health)
031.
{
032.
this
.name = name;
033.
this
.health = health;
034.
}
035.
036.
public
void
Attach(UnitViewer investor)
037.
{
038.
unitViewers.Add(investor);
039.
}
040.
041.
public
void
Detach(UnitViewer investor)
042.
{
043.
unitViewers.Remove(investor);
044.
}
045.
046.
public
void
Notify()
047.
{
048.
foreach
(UnitViewer unitviewr
in
unitViewers)
049.
{
050.
unitviewr.Update(
this
);
051.
}
052.
}
053.
054.
055.
public
int
Health
056.
{
057.
get
{
return
health; }
058.
set
059.
{
060.
health = value;
061.
Notify();
062.
}
063.
}
064.
065.
public
string
Name
066.
{
067.
get
{
return
name; }
068.
}
069.
}
070.
071.
class
Marine : Unit
072.
{
073.
public
Marine(
string
name,
int
health)
074.
:
base
(name, health)
075.
{
076.
}
077.
}
078.
079.
080.
interface
UnitViewer
081.
{
082.
void
Update(Unit unit);
083.
}
084.
085.
086.
class
MainScreen : UnitViewer
087.
{
088.
private
Unit unit;
089.
090.
public
void
Update(Unit _unit)
091.
{
092.
this
.unit = _unit;
093.
Console.WriteLine(
"메인화면 {0} 상태 변경 : 체력 {1}"
,
this
.unit.Name,
this
.unit.Health.ToString());
094.
}
095.
096.
public
Unit Unit
097.
{
098.
get
{
return
unit; }
099.
set
{ unit = value; }
100.
}
101.
}
102.
103.
class
StatusScreen : UnitViewer
104.
{
105.
private
Unit unit;
106.
107.
public
void
Update(Unit _unit)
108.
{
109.
this
.unit = _unit;
110.
Console.WriteLine(
"상태창 {0} 상태 변경 : 체력 {1}"
,
this
.unit.Name,
this
.unit.Health.ToString());
111.
}
112.
113.
public
Unit Unit
114.
{
115.
get
{
return
unit; }
116.
set
{ unit = value; }
117.
}
118.
}
119.
120.
class
EnemyScreen : UnitViewer
121.
{
122.
private
Unit unit;
123.
124.
public
void
Update(Unit _unit)
125.
{
126.
this
.unit = _unit;
127.
Console.WriteLine(
"적 상태창 {0} 상태 변경 : 체력 {1}"
,
this
.unit.Name,
this
.unit.Health.ToString());
128.
}
129.
130.
public
Unit Unit
131.
{
132.
get
{
return
unit; }
133.
set
{ unit = value; }
134.
}
135.
}
136.
}
137.
}
-UnitViewer를 인터페이스로 선언한 것은 참조되는 클래스들이 연관성이 많이 없을 때 유용하다.
굳이 UnitViewer를 상속해야하는 제한에서 벗어나서 마린의 객체를 참조하고자 하는 클래스들은 옵저버 인터페이스를 구현만 하면
상속받은 객체가 아니라도 마린의 상태를 볼 수 있게된다. (즉 더 유연한 구현을 할수가 있게된다.)
4. 실행결과
5. 피드백
-옵져버 패턴은 현업에서도 자주 사용될 수있는 패턴인듯하다. 특히 사용자에게 자료를 여러군데에서 보여줄때, 자료가 수정되면
열려진 창에서 모두데이터가 수정되게 하는 부분에서 유용할 듯 하다.
[참조]
-http://www.dofactory.com
-Head First Design Patterns
'Unity3D' 카테고리의 다른 글
OnApplicationQuit (0) | 2014.03.25 |
---|---|
monodevelop line endings policy (0) | 2014.03.24 |
[Unity] Basic Character Customization Tutorial (0) | 2014.03.20 |
I18N.CJK.dll (0) | 2014.03.20 |
Unity3d + Node.js 연동 테스트 (0) | 2014.03.17 |