dev.log2006. 9. 16. 05:18

생물이 시간에 따라 진화한다고는 하지만, 코드 역시 시간에 따라 진화한다. 물론, '진화'가 '진보'를 의미하지는 않으므로, 코드 역시 '퇴보'할 수 있다. 특히 잠깐 신경을 쓰지 않고 코드를 쓰다 보면, 디자인이 퇴보하는 것은 어렵지 않게 볼 수 있다. (열역학제2법칙을 떠올려 보자)

어떤 개발자가 음악을 플레이해볼 수 있는 간단한 툴을 만들고자 했다. 필요한 기능은 그냥 음악을 '틀어보고' '꺼보는' 기능이다. 그래서 다음과 같이 디자인했다.

class music
{
public:
    music();
    ~music();

    vod update();
    void play( char* name );
    void stop();

protected:
    void* data;
    bool playing;
};

 

동작은 간단하다.

1) play()멤버를 호출하면 name으로 data를 만들어내고, playing 멤버를 true로 바꾼다,

2) stop()멤버는 playing멤버를 false로 바꾸고 data를 지운다. 

3) update()은 playing이 true일 때만 data를 읽어서 재생을 계속한다.정말 간단하다.

 

잘 써먹고 있다가 보니 '일시정지'와 '계속재생'이 필요해졌다. 그래서 '일시정지' 기능을 다음과 같이 추가했다.

class music
{
public:
    music();
    ~music();

    vod update();
    void play();
    void stop();
    void resume();
    void pause();

protected:
    void* data;
    bool playing;
    bool paused;
};

동작은 아까와 크게 다름없다.

1) play(), stop()은 똑같다

2) pause()를 부르면, paused멤버를 true로 세팅한다.

3) resume()을 부르면, paused멤버를 false로 세팅한다.

4) update()에서는, paused가 false, playing 멤버가 true일 때만 재생을 계속한다.

 

이렇게 잘 써먹고 있다가 어느 기획자가 오더니 "페이드-인, 페이드-아웃이 반드시 있어야 되는데요"라고 말하고 가버린다. 그래서 이 개발자는 다음과 같이 페이드-인/아웃을 추가했다.

class music
{
public:
    music();
    ~music();

    vod update();
    void play();
    void stop();
    void resume();
    void pause();
    void fadein( float duration );
    void fadeout( float duration );

protected:
    void* data;
    bool playing;
    bool paused;

    int fade_mode;
    float fade_timer;
    float fade_duration;
};

이제 슬슬 골치가 아파오기 시작한다.

update()에서는, playing이 true, paused가 false일 때만 업데이트를 계속하되, fade_mode를 보면서 시간에 따라 볼륨을 조절해야 한다. 대략 다음과 같이 된다.

void music::update()
{
    if ( playing )
    {
         if ( !paused )
         {
             switch ( fade_mode )
             {
             case FADE_IN:    increase_volume(); break;
             case FADE_OUT: decrease_volume(); break;
             default: continue_playing();
             }
         }
    }
}

여기가 끝이 아니다. 어느날 어떤 기획자가 와서 말한다; "페이드-아웃할 때 음악을 끄지 말고 그냥 일시정지상태로도 할수 있게 해주세요"라고. 또 다른 기획자가 와서는 "3초간 대기한 후에 페이드-인 하면서 시작할 수 있게 해주세요. 아, 대기시간3초는 바뀔 수도 있어요"라고 말하고 가버린다. 그리고 기타 등등등.. 결과는? 걸레가 된 코드다.

 

제발 이 디자인에 문제가 없다고 생각하는 사람이 없기를 빈다. 구멍이 숭숭 뚫려있는 것이 눈에 보인다.

1) 이 객체가 처할 수 있는 모든 가능한 상태에 대한 고려가 없다 : playing이 false이거나, paused가 true일 때의 처리가 명시적이지 않다. 물론 지금이야 코드량이 워낙 적으니 명확하게 알아볼 수 있지만, 코드가 길어지면 명시적이지 않다는 것은, 객체가 알 수 없는 행동을 할 것이라고 봐야 한다. 두더지 게임기의 두더지처럼 버그를 하나 고치면 또 다른 버그가 끝도 없이 튀어나올 것이다 -_-

2) 이 객체에 다른 기능이 추가될 경우엔 구조가 바뀌어야 한다 : 만약 여기에 또 다른 기능, 이를테면 '2배속재생'이라든지, '거꾸로재생' 같은 기능이 추가되어야 한다면 또다시 if의 끝없는 중첩으로 해결할 속셈인 것이다, 이 개발자는. 프로그램의 구조는, 앞으로 발생 가능한 모든 사태를 포용할 수 있는 범용성을 갖추어야 한다. 기능이 추가될때 구조가 바뀌어야 하는 디자인은, 결코 디자인이라고 할 수 없다. 집안에 에어콘을 놓겠다고 벽을 허무는 셈이다. -_-

 

이렇게 짜는 사람이 있을 거라고 생각되지 않을지도 모르지만, 놀랍게도, 이 디자인은 회사에서 쓰고 있는 엔진에 그대로 들어있다. 수십만달러를 받아먹고 (내 어렴풋한 기억에는 50만달러정도였던거 같다) 파는 엔진에도 이런 디자인이 있는 걸 보면, 프로그래머가 밥벌어먹고 살 구석은 아직도 많은 거 같기도 하다 -_-;;


Posted by uhm