Chain of Responsibility 패턴은 어떠한 요청을 다음 대상에게 넘겨주는 패턴으로 여러 이벤트를 체인을 거치면서 처리하게 됩니다.
예제 코드를 알아보기 전에 golang의 embedding에 관한 특성 중 하나를 알아야 할게 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Monster interface {
Attack()
}
type Goblin struct {}
func (g *Goblin) Attack() {
fmt.Println("goblin attacks!")
}
type GoblinKing struct {
Goblin
}
|
cs |
위 코드에서 GoblinKing 구조체에서 Goblin을 embedding 하였습니다.
이렇게 하면 GoblinKing 객체에서 Goblin의 메서드들을 사용할 수 있는데
이는 Goblin이 정의한 interface도 적용된다는 뜻입니다.
즉, Goblin이 Monster 타입이므로 GoblinKing 역시 Monster 타입이 적용됩니다.
여기서 흥미로운 점은 아래와 같은 코드도 작성할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type GoblinKing struct {
Goblin
}
func (gk *GoblinKing) Attack() {
fmt.Println("goblinKing attacks!")
gk.Goblin.Attack()
}
func Test(monster Monster) {
monster.Attack()
}
func main() {
gk := GoblinKing{Goblin{}}
Test(&gk)
}
|
cs |
GoblinKing의 메서드로 Goblin이 가지고 있는 Attack 메서드를 정의하고
거기서 Goblin의 Attack 메서드도 사용할 수 있습니다.
코드를 실행해보면 "goblinKing attacks!"와 "goblin attacks!" 가 모두 출력됩니다.
이제 Chain of Responsibility의 예제 코드를 알아보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Monster struct {
name string
attack int
defense int
}
func NewMonster(name string, attack, defense int) *Monster {
return &Monster{name, attack, defense}
}
func (m Monster) String() string {
return fmt.Sprintf("%s stat: (%d %d)", m.name, m.attack, m.defense)
}
|
cs |
Monster 구조체를 정의하고 나중에 Print함수를 이용하여 결과를 보기 쉽게 하기 위하여 String 메서드를 정의하였습니다.
** 여기서 Monster는 맨 위의 코드와는 달리 struct 입니다.
1
2
3
4
|
type Modifier interface {
AddAbility(m Modifier)
Apply()
}
|
cs |
Modifier 인터페이스를 정의합니다.
AddAbility는 Monster의 attack 또는 defense를 증가시키고,
Apply는 한번 사용하면 그 때까지 chain들에 있는 Ability들을 적용시켜줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
type MonsterModifier struct {
monster *Monster
next Modifier
}
func NewMonsterModifier(monster *Monster) *MonsterModifier {
return &MonsterModifier{monster: monster}
}
func (monsterModifier *MonsterModifier) AddAbility(m Modifier) {
if monsterModifier.next != nil {
monsterModifier.next.AddAbility(m)
} else {
monsterModifier.next = m
}
}
func (monsterModifier *MonsterModifier) Apply() {
if monsterModifier.next != nil {
monsterModifier.next.Apply()
}
}
|
cs |
위 코드가 핵심이 되는 코드입니다.
MonsterModifier 구조체의 next는 일종의 LinkedList 형태가 될 것입니다.
AddAbility는 공격력 또는 방어력 증가에 대해 계속해서 더해나가고,
Apply함수는 현재 next에 LinkedList 형태로 되어있는 모든 공격력 또는 방어력 증가를 적용시킬 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
type MonsterAttackModifier struct {
MonsterModifier
}
func NewMonsterAttackModifier(monster *Monster) *MonsterAttackModifier {
return &MonsterAttackModifier{MonsterModifier{monster: monster}}
}
func (attackModifier *MonsterAttackModifier) Apply() {
attackModifier.monster.attack += 5
attackModifier.MonsterModifier.Apply()
}
|
cs |
이제 공격력을 추가시키기 위한 구조체입니다.
MonsterAttackModifier는 MonsterModifier를 embedding하지만
Apply 메서드를 다시 정의해서 공격력을 5 증가시켜줍니다.
그리고는 embedding하고 있는 MonsterModifier의 Apply 메서드를 실행하여 chain에 속한 다른 것들도 적용시켜줍니다.
MonsterDefenseModifier 역시 동일하게 작성하면 되므로 생략하겠습니다.
1
2
3
4
5
6
7
8
9
|
func main() {
dragon := NewMonster("dragon", 10, 10)
root := NewMonsterModifier(dragon)
root.AddAbility(NewMonsterAttackModifier(dragon))
root.AddAbility(NewMonsterAttackModifier(dragon))
root.AddAbility(NewMonsterAttackModifier(dragon))
root.Apply()
fmt.Println(dragon)
}
|
cs |
위 코드를 실행하면 공격력 증가가 총 3번이 다 적용되어
"dragon stat: (25 10)" 의 결과가 나오게 됩니다.
'golang > design pattern' 카테고리의 다른 글
golang design pattern #13 Observer (0) | 2021.11.23 |
---|---|
golang design pattern #10 Proxy (0) | 2021.11.15 |
golang design pattern #9 Flyweight (0) | 2021.11.14 |
golang design pattern #8 Decorator (0) | 2021.11.13 |
golang design pattern #7 Composite (0) | 2021.11.12 |