리팩터링 기법. 기본형을 객체로 바꾸기(Replace Primitive with Object)
기본형을 객체로 바꾸기(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 클래스 내부에 멤버 함수를 정의해주기만 하면 됩니다.