리팩터링/리팩터링 기법

리팩터링 기법. 기본형을 객체로 바꾸기(Replace Primitive with Object)

developer-tj 2023. 4. 1. 12:00
반응형

기본형을 객체로 바꾸기(Replace Primitive with Object)는 기본형 변수를 사용하는 대신 새로운 클래스를 정의하고 그 클래스의 인스턴스를 사용하는 방식으로 코드를 개선하는 리팩터링 기법입니다.

기본형 변수는 더 큰 데이터 구조와 연결되어 있지 않기 때문에 코드의 가독성과 유지보수성을 저해할 수 있습니다.
반면, 객체는 속성과 행동을 갖는 더 복잡한 데이터 구조를 나타낼 수 있습니다.

예를 들어, 기본형 변수로 통화를 나타내는 경우, 환율이나 기타 통화와 관련된 정보를 캡슐화하고, 통화 간의 변환을 수행할 수 있는 새로운 클래스를 정의할 수 있습니다.

또 다른 예로, 기본형 변수로 날짜를 나타내는 경우, 시간대, 일괄처리 및 기타 관련 정보를 캡슐화하고, 날짜 간의 비교, 연산 및 계산을 수행할 수 있는 새로운 클래스를 정의할 수 있습니다.

이 기법은 코드의 가독성, 유지보수성, 확장성을 향상시킬 수 있습니다.
하지만, 객체로 바꾸는 것은 기본형 변수를 사용하는 것보다 조금 더 많은 코드를 작성해야 하기 때문에 일부 개발자들은 이 기법을 사용하지 않는 경우도 있습니다.


기본형을 객체로 바꾸는 예시로 학생의 시험 점수를 저장하는 시스템이 있다고 가정해봅시다.


class Student {
public:
    Student(std::string name, int math_score, int science_score, int english_score)
        : name_(name), math_score_(math_score), science_score_(science_score), english_score_(english_score) {}

    std::string GetName() const { return name_; }
    int GetMathScore() const { return math_score_; }
    int GetScienceScore() const { return science_score_; }
    int GetEnglishScore() const { return english_score_; }

private:
    std::string name_;
    int math_score_;
    int science_score_;
    int english_score_;
};

Student 클래스는 시험 점수를 int로 저장하고 있습니다.
시험 점수를 객체로 바꾸어 보겠습니다.

먼저, 학생의 시험 점수를 저장할 Score 클래스를 정의합니다.


class Score {
public:
    Score(int math, int science, int english) : math_(math), science_(science), english_(english) {
        if (math_ < 0 || math_ > 100) {
            throw std::invalid_argument("math score should be between 0 and 100.");
        }
        if (science_ < 0 || science_ > 100) {
            throw std::invalid_argument("science score should be between 0 and 100.");
        }
        if (english_ < 0 || english_ > 100) {
            throw std::invalid_argument("english score should be between 0 and 100.");
        }
    }

    void setMath(int math) {
        if (math < 0 || math > 100) {
            throw std::invalid_argument("math score should be between 0 and 100.");
        }
        math_ = math;
    }

    void setScience(int science) {
        if (science < 0 || science > 100) {
            throw std::invalid_argument("science score should be between 0 and 100.");
        }
        science_ = science;
    }

    void setEnglish(int english) {
        if (english < 0 || english > 100) {
            throw std::invalid_argument("english score should be between 0 and 100.");
        }
        english_ = english;
    }

    int math() const { return math_; }
    int science() const { return science_; }
    int english() const { return english_; }

    double average() const { return static_cast(math_ + science_ + english_) / 3; }

private:
    int math_;
    int science_;
    int english_;
};


각 학생의 정보를 저장할 Student 클래스를 정의합니다.
Student 클래스는 이름과 Score 객체를 멤버 변수로 가지고 있습니다.


class Student {
public:
    Student(const std::string& name, const Score& score) : name_(name), score_(score) {}

    const std::string& name() const { return name_; }
    const Score& score() const { return score_; }

private:
    std::string name_;
    Score score_;
};

이제, main 함수에서 학생 정보를 생성하고 출력해보겠습니다.


int main() {
    Score score1(90, 85, 95);
    Student student1("Alice", score1);

    Score score2(80, 75, 85);
    Student student2("Bob", score2);

    std::cout << student1.name() << "'s average score: " << student1.score().average() << std::endl;
    std::cout << student2.name() << "'s average score: " << student2.score().average() << std::endl;

    return 0;
}

기존에는 각 학생의 시험 점수를 int형으로 저장했지만, Score 클래스를 사용하여 각 과목의 점수를 멤버 변수로 저장하고 평균 점수를 계산할 수 있도록 했습니다.
이렇게 하면 데이터를 더욱 직관적으로 표현할 수 있으며, Score 클래스에서 추가적인 연산이 필요할 경우 Score 클래스 내부에 멤버 함수를 정의해주기만 하면 됩니다.