리팩터링 기법. 타입 코드를 서브클래스로 바꾸기(Replace Type Code with Subclasses)
타입 코드(Type Code)란, 하나의 클래스 안에서 여러 종류의 객체를 표현하기 위해 사용하는 코드를 말합니다.
이는 예를 들어, 도형 클래스 안에서 도형의 종류를 표시하기 위한 숫자나 문자열과 같은 것을 말할 수 있습니다.
타입 코드를 서브클래스로 바꾸기(Replace Type Code with Subclasses)는 이러한 타입 코드를 사용하는 것을 서브클래스로 분리하는 방법입니다.
타입 코드에 따라 객체의 특성이 달라지는 것을 서브클래스로 만들어 표현함으로써, 객체지향적인 설계를 더욱 강화할 수 있습니다.
서브클래스로 분리하는 방법은, 기존 클래스의 타입 코드에 해당하는 각 서브클래스를 만든 다음, 기존 클래스에서 해당 타입 코드를 제거하고 대신 적절한 서브클래스의 인스턴스를 생성하여 반환하도록 수정하는 것입니다.
이렇게 하면 기존 클래스의 복잡도를 낮추고, 타입 코드에 따라 달라지는 동작을 서브클래스 내부에서 캡슐화함으로써 코드 유지보수성을 높일 수 있습니다.
예를 들어, 도형 클래스에서 타입 코드로 "사각형", "원", "삼각형" 등을 사용하고 있다면, 이를 각각의 서브클래스로 만들어 분리할 수 있습니다.
이렇게 하면 각 도형의 특성이 달라지는 경우에도 새로운 서브클래스를 추가하거나 수정하는 것만으로도 쉽게 처리할 수 있습니다.
다음은 도형 클래스에서 타입 코드로 "사각형", "원", "삼각형"을 사용하는 예제 코드가 있습니다.
enum class ShapeType {
kRectangle,
kCircle,
kTriangle
};
class Shape {
public:
Shape(ShapeType type, double width, double height, double radius, double base, double altitude)
: type_(type), width_(width), height_(height), radius_(radius), base_(base), altitude_(altitude) {}
double area() const {
switch (type_) {
case ShapeType::kRectangle:
return width_ * height_;
case ShapeType::kCircle:
return PI * radius_ * radius_;
case ShapeType::kTriangle:
return 0.5 * base_ * altitude_;
default:
return 0.0;
}
}
double perimeter() const {
switch (type_) {
case ShapeType::kRectangle:
return 2 * (width_ + height_);
case ShapeType::kCircle:
return 2 * PI * radius_;
case ShapeType::kTriangle:
return base_ + 2 * sqrt(base_ * base_ + 0.25 * altitude_ * altitude_);
default:
return 0.0;
}
}
private:
ShapeType type_;
double width_;
double height_;
double radius_;
double base_;
double altitude_;
};
타입 코드를 서브클래스로 바꾸기를 적용합니다.
class Shape {
public:
virtual double area() const = 0;
virtual double perimeter() const = 0;
};
class Rectangle : public Shape {
public:
Rectangle(double width, double height) : width_(width), height_(height) {}
double area() const override { return width_ * height_; }
double perimeter() const override { return 2 * (width_ + height_); }
private:
double width_;
double height_;
};
class Circle : public Shape {
public:
Circle(double radius) : radius_(radius) {}
double area() const override { return PI * radius_ * radius_; }
double perimeter() const override { return 2 * PI * radius_; }
private:
double radius_;
};
class Triangle : public Shape {
public:
Triangle(double base, double altitude) : base_(base), altitude_(altitude) {}
double area() const override { return 0.5 * base_ * altitude_; }
double perimeter() const override { return base_ + 2 * sqrt(base_ * base_ + 0.25 * altitude_ * altitude_); }
private:
double base_;
double altitude_;
};
위의 코드에서 Shape 클래스를 추상 클래스로 만들고 area()와 perimeter() 함수를 순수 가상 함수로 선언합니다.
그리고 Rectangle, Circle, Triangle 클래스를 Shape 클래스를 상속받아 만듭니다.
이렇게 하면 Shape 클래스를 사용하는 곳에서는 객체의 실제 타입을 알 필요 없이 다형성을 이용해 area()와 perimeter() 함수를 호출할 수 있습니다.
이렇게 하면 새로운 도형을 추가하더라도 Shape 클래스를 수정하지 않고 새로운 서브 클래스를 만들어서 추가할 수 있습니다.