소프트웨어 개발에서 의존성을 제거하거나 줄이는 작업은 유지보수성과 테스트 가능성을 크게 향상시킵니다. 특히, "메소드 객체 추출(Break Out Method Object)" 기법은 기존의 복잡한 메서드를 분리하여 단일 책임 원칙(Single Responsibility Principle)을 따르는 독립된 클래스로 재구성하는 유용한 방법입니다. 이 글에서는 메소드 객체 추출의 개념, 이점, 적용 방법 및 이를 활용한 테스트 코드 작성법을 살펴봅니다.
메소드 객체 추출(Break Out Method Object)란?
기존의 클래스가 지나치게 복잡한 메서드를 포함하고 있을 때, 해당 메서드를 별도의 클래스로 추출하여 책임을 분리하고 코드를 단순화하는 기법입니다. 이를 통해 다음과 같은 효과를 얻을 수 있습니다:
- 의존성 제거: 클래스 간 결합도를 낮추어 유연성을 향상.
- 테스트 용이성: 독립된 메서드 객체는 테스트하기 쉬운 단위가 됨.
- 코드 가독성 및 유지보수성 향상: 메서드의 로직을 별도 클래스로 분리하여 명확하게 정의.
메소드 객체 추출 적용 예제
기존 코드
class GDIBrush {
public:
void draw(vector& renderingRoots, ColorMatrix& colors, vector& selection);
private:
void drawPoint(int x, int y, COLOR color);
};
void GDIBrush::draw(vector& renderingRoots, ColorMatrix& colors, vector& selection) {
for(auto it = renderingRoots.begin(); it != renderingRoots.end(); ++it) {
point p = *it;
drawPoint(p.x, p.y, colors[n]);
}
}
변경된 코드
class PointRenderer {
public:
virtual void drawPoint(int x, int y, COLOR color) = 0;
};
class GDIBrush : public PointRenderer {
public:
void drawPoint(int x, int y, COLOR color) override;
};
class Renderer {
private:
PointRenderer* pointRenderer;
vector& renderingRoots;
ColorMatrix& colors;
vector& selection;
public:
Renderer(PointRenderer* renderer, vector& renderingRoots, ColorMatrix& colors, vector& selection)
: pointRenderer(renderer), renderingRoots(renderingRoots), colors(colors), selection(selection) {}
void draw();
};
void Renderer::draw() {
for(auto it = renderingRoots.begin(); it != renderingRoots.end(); ++it) {
point p = *it;
pointRenderer->drawPoint(p.x, p.y, colors[n]);
}
}
기법의 주요 변화와 이점
- 단일 책임 분리: GDIBrush는 이제 drawPoint 메서드만 담당하고, Renderer가 실제 렌더링 로직을 담당.
- 테스트 가능성: Renderer 클래스는 PointRenderer를 인터페이스로 받아 모킹(Mock)하여 독립적으로 테스트 가능.
- 확장성: 새로운 PointRenderer 구현체를 쉽게 추가 가능.
테스트 코드 작성
테스트 가능한 코드 구조로 변경한 후, 단위 테스트를 작성하여 각 클래스의 기능을 검증할 수 있습니다. 아래는 Renderer 클래스에 대한 간단한 테스트 코드입니다:
#include <gtest/gtest.h>
#include "Renderer.h"
#include "MockPointRenderer.h"
TEST(RendererTest, DrawPointsCorrectly) {
// MockPointRenderer는 PointRenderer를 상속받아 테스트 목적으로 동작을 검증
MockPointRenderer mockRenderer;
vector renderingRoots = {{0, 0}, {1, 1}, {2, 2}};
ColorMatrix colors = ...; // 테스트 데이터를 초기화
vector selection = ...;
Renderer renderer(&mockRenderer, renderingRoots, colors, selection);
// Act
renderer.draw();
// Assert
ASSERT_EQ(mockRenderer.getDrawnPoints().size(), renderingRoots.size());
EXPECT_EQ(mockRenderer.getDrawnPoints()[0], point(0, 0));
}
위 테스트 코드는 MockPointRenderer를 활용하여 Renderer가 호출한 drawPoint 메서드를 검증합니다. 이를 통해 Renderer 클래스의 로직이 예상대로 작동함을 확인할 수 있습니다.
5. 결론
"메소드 객체 추출"은 복잡한 메서드를 독립적인 객체로 분리하여 코드를 단순화하고 테스트 가능성을 향상시키는 강력한 리팩토링 기법입니다. 이를 통해 우리는 기존 코드를 안전하게 개선하고 유지보수 가능한 구조로 전환할 수 있습니다. 또한, 위와 같은 구조 변경은 향후 확장성과 협업에도 긍정적인 영향을 미칩니다.
이 글에서 제시한 기법과 예제를 활용하여 여러분의 코드 베이스를 한 단계 더 나아가게 만들길 바랍니다.
'SW 개발 일반 > 레거시코드와 놀기' 카테고리의 다른 글
레거시 코드와 놀기: 정적 메소드 드러내기 (Expose Static Method) (0) | 2025.01.22 |
---|---|
느리지만 그나마 쉬운 테스트 개발하기 (0) | 2025.01.20 |
레거시 코드와 놀기: 전역 참조 캡슐화 (Encapsulate Global Reference) (0) | 2025.01.20 |
레거시 코드와 놀기: 매개변수 적응 기법 (Adapt Parameter) (0) | 2025.01.17 |
코드 리뷰와 멘탈 모델의 중요성: 더 나은 소프트웨어를 위한 기틀 (0) | 2025.01.16 |