[읽기전에]
UML 다이어그램 : UML클래스 다이어그램 기본상식 http://hongjinhyeon.tistory.com/25
포스팅되는 디자인 패턴의 예는 스타크래프트를 기본으로 하였습니다 : 디자인 패턴을 시작하며 http://hongjinhyeon.tistory.com/24
<기본 정의 >
1. Iterator Pattern 정의
-컬렉션 구현 방법을 노출시키지 않으면서 그 집합체 안에 들어잇는 모든 항목에 접근할 수 있게 해주는 방법을 제공한다.
(Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.)
2. UML Diagram
3. 사용용도 및 장점
-반복자 패턴을 이용하면 집합체 내에서 어떤 식으로 일이 처리되는지 대해서 전혀 모르는 상태에서 그 안에
있는 모든 항목들에 대해서 반복작업을 수행할 수 있다.
-클래스가 데이터를 저장함에 있어서 배열,ArrayList,해쉬테이블 등등의 데이터 구조를 사용한다 할지라도 이터레이터
인터페이스를 구현하게 함으로써 외부에서는 동일한 인터페이스로 각각의 항목에 대해서 순차적으로 접근이 가능하다.
4. 소스
001.
using
System;
002.
using
System.Collections;
003.
004.
namespace
DoFactory.GangOfFour.Iterator.Structural
005.
{
006.
/// <summary>
007.
/// MainApp startup class for Structural
008.
/// Iterator Design Pattern.
009.
/// </summary>
010.
class
MainApp
011.
{
012.
/// <summary>
013.
/// Entry point into console application.
014.
/// </summary>
015.
static
void
Main()
016.
{
017.
ConcreteAggregate a =
new
ConcreteAggregate();
018.
a[0] =
"Item A"
;
019.
a[1] =
"Item B"
;
020.
a[2] =
"Item C"
;
021.
a[3] =
"Item D"
;
022.
023.
// Create Iterator and provide aggregate
024.
ConcreteIterator i =
new
ConcreteIterator(a);
025.
026.
Console.WriteLine(
"Iterating over collection:"
);
027.
028.
object
item = i.First();
029.
while
(item !=
null
)
030.
{
031.
Console.WriteLine(item);
032.
item = i.Next();
033.
}
034.
035.
// Wait for user
036.
Console.ReadKey();
037.
}
038.
}
039.
040.
/// <summary>
041.
/// The 'Aggregate' abstract class
042.
/// </summary>
043.
abstract
class
Aggregate
044.
{
045.
public
abstract
Iterator CreateIterator();
046.
}
047.
048.
/// <summary>
049.
/// The 'ConcreteAggregate' class
050.
/// </summary>
051.
class
ConcreteAggregate : Aggregate
052.
{
053.
private
ArrayList _items =
new
ArrayList();
054.
055.
public
override
Iterator CreateIterator()
056.
{
057.
return
new
ConcreteIterator(
this
);
058.
}
059.
060.
// Gets item count
061.
public
int
Count
062.
{
063.
get
{
return
_items.Count; }
064.
}
065.
066.
// Indexer
067.
public
object
this
[
int
index]
068.
{
069.
get
{
return
_items[index]; }
070.
set
{ _items.Insert(index, value); }
071.
}
072.
}
073.
074.
/// <summary>
075.
/// The 'Iterator' abstract class
076.
/// </summary>
077.
abstract
class
Iterator
078.
{
079.
public
abstract
object
First();
080.
public
abstract
object
Next();
081.
public
abstract
bool
IsDone();
082.
public
abstract
object
CurrentItem();
083.
}
084.
085.
/// <summary>
086.
/// The 'ConcreteIterator' class
087.
/// </summary>
088.
class
ConcreteIterator : Iterator
089.
{
090.
private
ConcreteAggregate _aggregate;
091.
private
int
_current = 0;
092.
093.
// Constructor
094.
public
ConcreteIterator(ConcreteAggregate aggregate)
095.
{
096.
this
._aggregate = aggregate;
097.
}
098.
099.
// Gets first iteration item
100.
public
override
object
First()
101.
{
102.
return
_aggregate[0];
103.
}
104.
105.
// Gets next iteration item
106.
public
override
object
Next()
107.
{
108.
object
ret =
null
;
109.
if
(_current < _aggregate.Count - 1)
110.
{
111.
ret = _aggregate[++_current];
112.
}
113.
114.
return
ret;
115.
}
116.
117.
// Gets current iteration item
118.
public
override
object
CurrentItem()
119.
{
120.
return
_aggregate[_current];
121.
}
122.
123.
// Gets whether iterations are complete
124.
public
override
bool
IsDone()
125.
{
126.
return
_current >= _aggregate.Count;
127.
}
128.
}
129.
}
-Iterator는 Aggregate 객체를 참조하여 해당 클래스의 ArrayList를 외부에서 순차적으로 확인이 가능합니다.
-순차적으로 이동 또는 스텝을 정해서 짝수에 해당하는 아이템만 리턴이 가능하게도 설정이 가능하며, 특정 아이템의 삭제도
가능하게 변경이 가능합니다. ( 사용용도에 따라서 확장이 가능 )
5. 실행결과
Iterating over collection:
Item A
Item B
Item C
Item D
< 실제 적용 >
1. UML Diagram
2. 사용용도 및 장점
-스타크래프트에서는 자신들의 유닛을 최대 10마리씩 10개의 번호에(1~10) 에 미리 지정할 수 있습니다. ( HotKey 기능:부대지정 ) 해당 번호를 누르면
저장된 유닛들이 모두 선택이 됩니다. 옵저버 유닛은 적의 클로킹된 유닛( 보이지 않는 유닛 ) 을 보여주게 하는 기능을 합니다. 옵져버의 영역
에 속한 적유닛들에 대한 리스트가 존재하게 됩니다.
플레이어의 현황을 관리하는 클래스에서는 위의 두 상황에서 1번 Hotkey에 저장된 유닛을 호출할 경우도 있으며, 옵져버가 밝히는 적 유닛의
리스트도 호출할 경우가 존재합니다. 이런경우에 이터레이터 패턴을 사용하게 되면, 플레이어의 현황을 관리하는 클래스에서는 HotKey나 Observer
내부의 상황을 몰라도 UnitIterator의 인터페이스로 모두 접근이 가능하게 됩니다.
-또한 다른 유닛들을 소유하는 클래스를 설계 할때 확장에도 용이합니다. UnitContainer를 상속받는 클래스를 추가후에, 해당 데이터 구조에 해당하는
UnitIterator를 구현하면 플레이어의 현황을 관리하는 클래스에서는 이전 소스와 동일하게 사용이 가능하게 됩니다.
3. 소스
001.
using
System;
002.
using
System.Collections.Generic;
003.
using
System.Linq;
004.
using
System.Text;
005.
006.
namespace
Iterator
007.
{
008.
class
Program
009.
{
010.
static
void
Main(
string
[] args)
011.
{
012.
HotKey HotKey1 =
new
HotKey();
013.
HotKey1.addUnit(
new
Unit(
"마린1"
));
014.
HotKey1.addUnit(
new
Unit(
"마린2"
));
015.
HotKey1.addUnit(
new
Unit(
"마린3"
));
016.
HotKey1.addUnit(
new
Unit(
"메딕1"
));
017.
HotKey1.addUnit(
new
Unit(
"메딕2"
));
018.
HotKey1.addUnit(
new
Unit(
"파이어뱃1"
));
019.
020.
UnitIterator Iterator = HotKey1.CreateIterator();
021.
022.
while
(Iterator.hasNext())
023.
{
024.
Console.WriteLine(
"핫키에 등록된 유닛:{0}"
, Iterator.Next().m_strName);
025.
}
026.
027.
Console.WriteLine(Environment.NewLine);
028.
029.
Observer Observer1 =
new
Observer();
030.
Observer1.addUnit(
new
Unit(
"적 다크템플러1"
));
031.
Observer1.addUnit(
new
Unit(
"적 다크템플러2"
));
032.
Observer1.addUnit(
new
Unit(
"적 다크템플러3"
));
033.
Observer1.addUnit(
new
Unit(
"적 레이쓰1"
));
034.
Observer1.addUnit(
new
Unit(
"적 레이쓰2"
));
035.
Observer1.addUnit(
new
Unit(
"적 고스트1"
));
036.
037.
Iterator = Observer1.CreateIterator();
038.
039.
while
(Iterator.hasNext())
040.
{
041.
Console.WriteLine(
"옵저버1이 밝혀진 클락킹된 유닛:{0}"
, Iterator.Next().m_strName);
042.
}
043.
044.
Console.ReadKey();
045.
}
046.
047.
/// <summary>
048.
/// 유닛 클래스 생성
049.
/// </summary>
050.
class
Unit
051.
{
052.
public
string
m_strName {
get
;
set
; }
053.
054.
public
Unit(
string
_strName)
055.
{
056.
m_strName = _strName;
057.
}
058.
}
059.
060.
/// <summary>
061.
/// 반복자 추상 클래스 생성
062.
/// </summary>
063.
abstract
class
UnitIterator
064.
{
065.
public
abstract
Unit Next();
066.
public
abstract
bool
hasNext();
067.
}
068.
069.
/// <summary>
070.
/// 핫키를 관리하는 반복자 클래스 생성
071.
/// </summary>
072.
class
HotkeyIterator : UnitIterator
073.
{
074.
075.
private
HotKey m_HotKey;
076.
private
int
m_intCurrent;
077.
078.
public
HotkeyIterator(HotKey _Hotkey)
079.
{
080.
m_HotKey = _Hotkey;
081.
m_intCurrent = 0;
082.
}
083.
084.
public
override
Unit Next()
085.
{
086.
return
m_HotKey[m_intCurrent++];
087.
}
088.
089.
public
override
bool
hasNext()
090.
{
091.
return
m_intCurrent < m_HotKey.Count();
092.
}
093.
094.
}
095.
096.
class
ObserverIterator : UnitIterator
097.
{
098.
private
Observer m_Observer;
099.
private
int
m_intCurrent;
100.
101.
public
ObserverIterator(Observer _Observer)
102.
{
103.
this
.m_Observer = _Observer;
104.
m_intCurrent = 0;
105.
}
106.
107.
public
override
Unit Next()
108.
{
109.
return
m_Observer[m_intCurrent++];
110.
}
111.
112.
public
override
bool
hasNext()
113.
{
114.
return
m_intCurrent < m_Observer.Count();
115.
}
116.
117.
}
118.
119.
/// <summary>
120.
/// 유닛을 관리하는 추상 클래스 생성
121.
/// </summary>
122.
abstract
class
UnitContainer
123.
{
124.
public
abstract
UnitIterator CreateIterator();
125.
}
126.
127.
/// <summary>
128.
/// 유닛을 소유하고 있는 핫키 클래스 생성
129.
/// </summary>
130.
class
HotKey : UnitContainer
131.
{
132.
private
Unit[] m_HotKeyUnit =
new
Unit[10];
133.
private
int
m_Count = 0;
134.
135.
public
override
UnitIterator CreateIterator()
136.
{
137.
return
new
HotkeyIterator(
this
);
138.
}
139.
140.
/// <summary>
141.
/// 유닛 추가 (배열)
142.
/// </summary>
143.
/// <param name="_Unit"></param>
144.
public
void
addUnit(Unit _Unit)
145.
{
146.
if
(m_Count<10)
147.
{
148.
this
.m_HotKeyUnit[m_Count] = _Unit;
149.
m_Count++;
150.
}
151.
152.
}
153.
154.
public
int
Count()
155.
{
156.
return
m_Count;
157.
}
158.
159.
public
Unit
this
[
int
_index]
160.
{
161.
get
{
return
m_HotKeyUnit[_index]; }
162.
}
163.
164.
}
165.
166.
/// <summary>
167.
/// 클라킹된 유닛을 소유하는 옵져버 클래스 생성
168.
/// </summary>
169.
class
Observer : UnitContainer
170.
{
171.
private
List<Unit> m_ObserverUnit =
new
List<Unit>();
172.
173.
public
override
UnitIterator CreateIterator()
174.
{
175.
return
new
ObserverIterator(
this
);
176.
}
177.
178.
public
void
addUnit(Unit _Unit)
179.
{
180.
this
.m_ObserverUnit.Add(_Unit);
181.
}
182.
183.
public
int
Count()
184.
{
185.
return
m_ObserverUnit.Count;
186.
}
187.
188.
public
Unit
this
[
int
_index]
189.
{
190.
get
{
return
m_ObserverUnit[_index]; }
191.
}
192.
}
193.
194.
}
195.
}
-HotKey와 Observer에서 Unit을 저장함에 있어서 각각 배열과 리스트를 사용하였습니다. HotKey는 최대 10개의 유닛만 사용이 가능하므로
10개로 멤버변수를 사용하며, Observer는 옵져버가 밝히는 영역의 모든 클라킹된 유닛을 보여주므로 리스트를 이용합니다.
-두개의 클래스에서 각각 다른 배열과 리스트를 사용한다 할지라도 Iterator에서는 동일하게 접근하여 처리가 가능합니다.
4. 실행결과
5. 피드백
-처음에는 별로 안중요하다고 생각이 들었지만 보면 볼수록 좋은 패턴같습니다.
-서로 다른 사람이 여러개의 클래스를 개발한다고 할때, 이터레이터의 인터페이스를 준수해서 구현을 하게되면 사용하는 곳에서는
항상 동일한 코드로 접근이 가능하니 편리한거 같습니다.
[참조]
-http://www.dofactory.com
-Head First Design Patterns
'프로젝트 설계 > 디자인 패턴' 카테고리의 다른 글
10. State Pattern ( 스테이트 / 상태 패턴 C# ) (4) | 2012.09.11 |
---|---|
9. Command Pattern ( 커맨드 패턴 C# ) (1) | 2012.09.11 |
8. Strategy Pattern ( 스트래티지 / 전략 패턴 C# ) (9) | 2012.09.06 |
7. Observer Pattern ( 옵져버 패턴 C# ) (3) | 2012.09.05 |
6. Facade Pattern (퍼사드 패턴 C# ) (2) | 2012.09.04 |