티스토리 뷰

 


안녕하세요, 제이입니다.

오늘은 디자인 패턴의 일종인 Strategy Pattern에 대해 정리 해 보겠습니다.

모든 Design Pattern 관련 포스팅은 아래 서적을 활용하여 직접 구현한 내용입니다.

head first design pattern에 대한 이미지 검색결과


정말 볼 때마다 초등학교 영어학원 서적을 생각나게 하는 디자인과 구성이 인상적인 헤드퍼스트 시리즈의 책을 활용하였습니다. 해당 서적은 JAVA로 구현되어 있지만, 저는 C++로 구현해 볼 예정입니다. 많이 부족한 개발자라, 언제나 지적은 환영합니다.


1. Strategy Pattern

 간단하게 말해서 실행 중에 알고리즘을 고를 수 있는 패턴입니다.

 가령 오리라는 객체가 있다고 합시다.


오리가 할 수 있는 일


1. 수영하기

2. 꽥꽥 울기

3. 뒤뚱뒤뚱 걸어다니기

4. 날아다니기

.

.


오리가 할 수 있는 일은 매우 많습니다.

이 할 수 있는 일을 가지고 오리라는 객체를 만들고, 상속을 받아 여러 종류의 오리를 표현하게 되면 여러 모로 효율적인 개발이 가능할겁니다. 이 오리 객체를 이용해서 여러 종류의 오리를 만들어 봅시다.

 

 

 흰 오리

 - 수영 가능

- 걷기 가능

- 울기 가능

- 날아다니기 가능

예쁜 오리 

 - 수영 가능

- 걷기 가능

 - 울기 가능

- 날아다니기 가능

 화려한 오리

- 수영 가능

- 걷기 가능

- 울기 가능

- 날아다니기 가능

러버덕

 - 수영 가능

- 걷기 불가능

- 울기 불가능

- 날아다니기 불가능


다른 오리들은 위의 모든 기능이 가능하지만, 일종의 오리인 러버덕은 울 수도 없고, 날아다닐 수도, 걸어다닐 수도 없는 오리입니다. 주어진 자원으로 러버덕을 만들어 본다고 가정을 해 봅시다.


 1) 상속으로 러버덕 구현하기

 - 오버라이드를 통해 함수를 재정의하게 되면 울지도 않는, 날지도 않는, 걸어다닐 수도 없는 오리를 구현할 수 있다.

 - 하지만 여러 오리의 행동을 정확하게 알기 어렵다.

 - 게다가, 굳이 나는 기능, 꽥꽥거리는 기능, 걸어다니는 기능이 필요없는데 이러한 기능들을 상속받아야 할 필요가 있을까? 


 2) 인터페이스로 구현

 - 굳이 필요없는 기능을 넣지 말자.

 - 나는 기능, 우는 기능 등을 따로 인터페이스로 분리해서 구현.


나는 기능, 우는 기능을 인터페이스로 분리해서 구현하게 되면, 일일이 흰 오리, 예쁜 오리, 화려한 오리의 나는 기능을 일일이 구현해야 합니다. 이 세 오리의 나는 기능, 우는 기능은 같기 때문에, 결국 코드의 중복만 늘리는 꼴이 됩니다.


오리를 보면, 우리가 장난감 오리를 포함해서 오리라고 부르는 것들의 공통적인 특징이 있습니다(납작하고 긴 부리, 날개가 있음...). 모든 오리의 공통적인 기능은 변하지 않습니다. 달라질 리 없는 부분은 놔 두고, 여기서 달라질 수 있는 기능만 분리를 시켜, 그때 그때 해당하는 객체가 달라질 수 있는 기능을 바꿔 끼울 수 있게 하는 것이 바로 이 Strategy Pattern입니다.


바뀌는 부분을 따로 뽑아서 캡슐화를 시켜 바뀌지 않는 부분에는 영향을 미치지 않게 구현을 하면 됩니다.



2. 구현

 - 바뀌지 않는 부분과 바뀌는 부분을 분리합니다. 저는 오리의 우는 행위와 나는 행위에 대해 분리를 하겠습니다. 클래스 다이어그램은 Visual Studio의 클래스 디자이너를 이용하였습니다.

 - C++은 뚜렷하게 인터페이스의 개념이 있는 것이 아니기 때문에 추상클래스로 각 행위들을 정의하였습니다.


 1) 오리의 우는 행위 (QuackBehavior)


 러버덕은 울지 못하지만 다른 오리들은 울 수 있습니다. 그래서 오리의 우는 행위 인터페이스를 만들고, Quack! 하고 우는 행위, 그리고 조용한 울음을 추가하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class QuackBehavior
{
public:
    QuackBehavior();
    ~QuackBehavior();
    virtual void Quack();
};
 
 
class QuackQuack : public QuackBehavior
{
public:
    QuackQuack();
    ~QuackQuack();
    void Quack();
};
 
class QuietDuck : public QuackBehavior
{
public:
    QuietDuck();
    ~QuietDuck();
    void Quack();
};
 
cs

 

 

 2) 오리의 나는 행위 (FlyBehavior)

오리 중에선 날 수 있는 오리도 있고, 날 수 없는 오리도 있습니다. 그래서 나는 행동 인터페이스를 정의하고, 그 밑에 나는 행위, 날지 않는 행위에 대해 정의해주었습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class FlyBehavior
{
public:
    FlyBehavior();
    virtual ~FlyBehavior() = 0;
    virtual void fly() = 0;
};
 
class FlyWithWing : public FlyBehavior
{
public:
    FlyWithWing();
    ~FlyWithWing();
    void fly();
};
 
class NotFly : public FlyBehavior
{
public:
    NotFly();
    ~NotFly();
    void fly();
};
 
 
cs


3) 오리 구현

 - 평범한 오리, 그리고 울 수도 없고 날 수도 없는 러버덕, 총 두 종류의 오리를 만들어 보겠습니다. 

 

수퍼클래스의 오리는 나는 행위 (mFBehavior), 그리고 우는 행위 (mQBehavior), 총 두 가지의 클래스를 가지고 있습니다. 그리고 오리가 날 수 있도록 FlyDuck이라는 함수를 추가해주었고, 오리가 울 수 있도록 QuackDuck이라는 함수를 추가 해 주었습니다.


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Duck
{
public:
    Duck();
    ~Duck();
    QuackBehavior *mQBehavior;
    FlyBehavior *mFBehavior;
    void FlyDuck();
    void QuackDuck();
};
 
class OrdinaryDuck : public Duck
{
public:
    OrdinaryDuck()
    {
        mFBehavior = new FlyWithWing();
        mQBehavior = new QuackQuack();
    }
    ~OrdinaryDuck();
};
 
class RubberDuck : public Duck
{
public:
    RubberDuck()
    {
        mQBehavior = new QuietDuck();
        mFBehavior = new NotFly();
    }
    ~RubberDuck();
};
 
cs

 

그리고 메인에서 Duck 객체를 선언할 때, RubberDuck을 선언했다면 RubberDuck의 나는 행위, 우는 행위가 발생하고, OrdinaryDuck을 선언했다면 Quack 하는 오리와 날아다니는 오리를 볼 수 있을 것입니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
    Duck *duck = new OrdinaryDuck();
 
    duck->FlyDuck();
    duck->QuackDuck();
 
 
    Duck *rubberDuck = new RubberDuck();
 
    rubberDuck->FlyDuck();
    rubberDuck->QuackDuck();
 
    return 0;
}
cs


위의 main을 실행시켜 보면,

어떤 객체를 생성시켰는가에 따라 다른 오리의 행동이 발생하는 것을 확인할 수 있습니다.

 

처음 이야기 했던 상속을 사용한다면 나중에 유지보수를 할 때 시간이 많이 걸리게 되고, 모든 행위를 인터페이스만으로 표현했을 때 코드의 중복이 늘어나게 됩니다. 이 때 Strategy Pattern을 사용하게 되면 우는 행위를 고친다면 우는 행위만 신경쓰기 때문에 유지보수에 용이한 코드, 그리고 추후에 새로운 알고리즘의 추가가 용이해집니다.

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함