삽질의 현장/- C++

#021_시(c)시(c)해서 C++?!_다시 한번 Command 패턴 사용하기

shovelman 2015. 7. 20. 01:09

안녕하세요 삽잡이입니다...


몇일 전에 커맨드 패턴, 그러니까 명령어 패턴에 대한 글을 올린 기억이 납니다...

헷갈리니까... 다시한번... 예제를 통해 살펴보도록 하겠습니다...

전체적인 코드보다는 이럴 때 사용하는 구나 하는 감을 잡기 위해... 부분적으로...

시작합니다...



우선...

멤버 하나하나를 담는 Unit이라는 class가 있다고 생각해봅시다...

이 Unit을 관리하는 UnitCollection 이라는 class를 만들어보도록 하죠...


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
 
 
#pragma once
#include <vector>
#include "Unit.h"
using namespace std;
 
class IIsEqual
{
public:
    virtual bool operator()(Unit *unit) = 0;
};
class ICompare
{
public:
    virtual int operator()(Unit *u1, Unit *u2) = 0;
};
 
class UnitCollection : private vector<Unit *>
{
public:
    void AddUnit(Unit *unit);
    Unit *Find(IIsEqual &ise);
    void Sort(ICompare &com);
    void List();
};
 
 
cs

Unit을 관리하기위해 Unit에 대해 알아야하니 해당 헤더를 포함시켰구요....
vector 표준 라이브러리르 포함시켰습니다...

여기서 주목해야할 점은 
IIsEqual, ICompare라는 순수 가상 메소드...즉 추상 클래스를 정의했다는 것입니다.

그 중에서도 함수 호출에 대한 오버로딩 함수를 정의했습니다.

이를 파생하는 클래스는 반드시 이에 대한 구현을 해야합니다.


그 아래 UnitCollection 클래스를 살펴본다면 vector 의 기능 중 

전체가 아닌 사용자에 입맛에 맞는 부분만을 사용하기 위해 맞춤형으로 메소드들을 구현했습니다.


또 여기서 Find와 Sort 메소드를 보시면 아시겠지만, 

모두 추상 메소드형의 인자를 받고 있습니다.


해당 메소드의 내용을 살펴보도록 하겠습니다...


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
 
 
Unit *UnitCollection::Find(IIsEqual &ise)
{
    UIter seek = begin();
    UIter last = end();
    Unit *unit = 0;
    for (; seek != last; ++seek)
    {
        if (ise(*seek))
        {
            unit = *seek;
            break;
        }
    }
    return unit;
}
void UnitCollection::Sort(ICompare &com)
{
    UnitCollection &na = (*this);
    for (int n = size(); n > 1; n--)
    {
        for (int i = 1; i < n; i++)
        {
            Unit *temp = na[i - 1];
            na[i - 1= na[i];
            na[i] = temp;
        }
    }
}
 
cs

다시 한번 말씀드리지만 모두 추상 메소드 형식의 인자를 받고 있습니다.

그리고 주목해야할 점은 바로 

if(ise(*seek))과 같이 사전에 구현한 함수호출 오버로딩에 의해서 자신을 불렀던 곳으로

다시 callback을 하는 것을 확인할 수 있습니다.


이처럼 공통적인 기능만을 구현해 두고 인자를 기반 클래스로 받는다면

효율적이며 해당 소스파일 자체가 가벼워질 수 있습니다....


어떤말인지 이해가 가시지 않는다면 아래의 코드를 참고하시길 바랍니다.


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
 
 
class EqualNum :public IIsEqual
{
    int num;
public:
    EqualNum(int num)
    {
        this->num = num;
    }
    virtual bool operator()(Unit *unit)
    {
        return unit->GetNum() == num;
    }
};
void App::FindByNum()
{
    int num = 0;
    cout << "번호:";
    cin >> num;
 
    EqualNum en(num);
    Unit *unit = uc->Find(en);
    if (unit == 0)
    {
        cout << "존재하지 않음" << endl;
        return;
    }
    unit->View();
}
 
 
cs

검색 기능과 정렬 기능을 가진 메소드들을 사전에 정의 해뒀습니다.

그리고 실제로 application 파일에서 이와 같은 기능을 호출해서 사용하려고 할 때

각각 검색하는 방법, 조건들이 다양할 수 있습니다.

이때 커멘드 패턴의 빛이 발휘되는 것입니다...


사용하는 기능마다 일일히 정의할 필요 없이

사전에 만든 IIsEqual, ICompare 과 같은 기반 클래스를 만들어 놓고

입맛에 맞게 파생 클래스로 다양한 조건들에 충족하는 클래스를 만드는 것입니다.


그렇다면 바로 위의 코드 중 Find(en) 처럼 메소드 호출을 통해

기반 클래스 형식으로 인자를 보냈어도 

결국에는 Find 메소드에서 Callback을 함으로 조건에 충족하는

결과를 수행할 수 있게 됩니다.


이와 같이 커맨드 패턴을 기반으로한 코드를 구현한다면

기반이 되는 기능으로 부터 여러 조건에 맞게 파생 클래스로 기능을 구현하여

재사용성을 높여 프로그램을 만들 수 있습니다.


위와 같이 IIsEqual, ICompare 처럼 기반이 되는 기능을 사전에 약속해두고

필요한 조건들에 맞게 파생클래스를 만든다는 것이죠...


지금껏 이 글을 읽으셨다면 아시겠지만

Find, Sort 메소드는 건들지도 않았습니다. 단지 조건에 맞는 파생 클래스만 구현하고

해당 메소드를 만들어 호출했을 뿐입니다...


이렇게 재사용할 수 있고, 기능 구현한 코드 또한 복잡하지 않고

간결하게 사용할 수 있어 메리트가 있군요...



객체지향이라는 세계는 참으로 오묘하고도

매력이 있는 세계 같습니다...


최근에 C언어를 사용할 일이 있어서 코딩을 하고 있었는데

확실히 C++에 손을 대다가 갑자기 C를 만지니 DownGrade 된듯한 느낌을 ㅎㅎ...

절차지향에 대한 개념을 무시하는 것은 아니지만

객체지향에 대한 편리함을 새삼 느끼는 시간이었습니다...


다음시간에 더욱 알찬 내용으로 찾아 뵙도록 하죠!


이상 삽잡이였습니다~!