본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] 디자인 패턴 : 변하는 것을 분리하는 방법 2가지

by 심찬 2022. 1. 30.

[C++] 디자인 패턴 : 변하는 것을 분리하는 방법 2가지

 

 

클래스를 정의하고 상속 받아서 사용하는 경우 재사용성을 고려하는 디자인 패턴에 대해 알아야 한다.

 

변하지 않는 코드(전체 흐름) 안에 있는 변해야 하는 부분(정책)은 분리 하는 것이 좋다.

 

 

1) template method : 변하는 것을 가상함수로 (상속 기반)

변해야 하는 부분 (아래에서 validate 함수)을 별도의 가상 함수로 분리한다.

변하는 것 (validate)을 가상 함수로 분리할 때의 장점

 - validation 정책을 변경하고 싶다면 Edit의 파생 클래스를 만들어서  validate() 가상 함수를 재정의하면 된다.

 

template method 장단점

상속 기반은 유연성이 떨어지는 편, 재사용이 크게 필요없는 경우 명확한 편.

템플릿 메소드에서 알고리즘의 골격을 정의

알고리즘의 여러 단계 중 일부는 서브 클래스에서 구현. 훅 메소드라고 한다. (hook method)

알고리즘의 구조를 유지하면서 서브 클래스에서 특정 단계를 재정의

 

코드 설명

주소값을 입력받을 때 AddressEdit는 char 형태는 단순히 true를 리턴하도록 설계되어 있다.

AddressEdit에서 validate가 재정의되어서 기존의 Edit에는 isDigit(c)가 동작하지 않게 된다.

또다른 class를 정의해서 다른 validate를 재정의해 활용할 수 있다.

#include <iostream>
#include <string>
#include <conio.h>

using namespace std;

class Edit
{
    string data;
public:
    virtual bool validate(char c){
        return isdigit(c);
    }
    virtual string getData(){
        data.clear();

        while( 1 ){
            char c = getch();
            if ( c == 13 ) break;
            if ( validate(c) ){
                data.push_back(c);
                std::cout << c;
            }
        }
        std::cout << endl;
        return data;
    }
};

class AddressEdit : public Edit {
public:
    virtual bool validate(char c) {
        return true;
    }
};

int main() {
    AddressEdit edit;
    while(1) {
        string s = edit.getData();
        cout << s << endl;
    }
}

주소 값을 입력 받을 때 validate가 항상 true를 반환함.

AddressEdit에서 처럼 Edit 클래스를 상속 받고 validate를 재정의하여 사용할 수 있다.

 

 

 

2) strategy 구성 : 변하는 것을 다른 클래스로 (전략 패턴 구성)

 

변하는 것을 다른 클래스로 분리할 때

: 교체 가능해야 한다 - 약한 결합, 인터페이스 기반 통신

: IValidator 인터페이스 설계

 

strategy 패턴 구성 장단점

알고리즘의 군을 정의하고 각각을 캡슐화 해서 교환해 사용할 수 있도록 만든다.

strategy 패턴을 활용하면 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.

 

 

코드 설명

값을 입력받을 때 5자의 숫자만 입력 받을 수 있도록 하는 코드입니다.

문자입력은 불가하고, 5자를 넘길수도 적게도 입력이 불가합니다.

오직 5자의 숫자만 입력했을 때 엔터키를 수용합니다.

isComplete에서 string의 사이즈가 5일 때만 true를 리턴합니다.

#include <iostream>
#include <string>
#include <conio.h>
using namespace std;

struct IValidator  // validation을 위한 인터페이스
{
    virtual bool validate(string s, char c) = 0;
    virtual bool iscomplete(string s)  { return true;}
    virtual ~IValidator() {}
};

class Edit
{
    string data;
    IValidator* pVal = 0;
public:
    void setValidator(IValidator* p ) { pVal = p;}

    string getData() {
        data.clear();
        while( 1 ) {
            char c = getch();
            if ( c == 13 &&
            ( pVal == 0 || pVal->iscomplete(data) )){
                break;
            }
            if ( pVal == 0 || pVal->validate(data, c) ) {
                data.push_back(c);
                cout << c;
            }
        }
        cout << endl;
        return data;
    }
};

class LimitDigitValidator : public IValidator {
    int value;
public:
    LimitDigitValidator(int n) : value(n) {}
    virtual bool validate( string s, char c ) {
        return s.size() < value && isdigit(c);
    }

    virtual bool iscomplete( string s) {
        return s.size() == value;
    }
};

int main() {
    Edit edit;
    LimitDigitValidator v(5);
    edit.setValidator(&v);

    while(1) {
        string s = edit.getData();
        cout << s << endl;
    }
}

* 동일 코드가 온라인 컴파일러에서는 제대로 동작하지 않아서 Dev C++로 실행시켰습니다.

 

5자의 숫자만을 입력 받도록 합니다.

 

* 둘 중 어느 것이 더 낫다고 말하기는 어렵고, 상황에 따라 맞는 디자인 패턴을 활용하도록 한다.

 

 

댓글