코드의 여백

[오브젝트] 9장 '유연한 설계', 왜 중요할까?

by rowing0328

※ 책 내용을 바탕으로 제 관점에서 풀어쓴 글입니다. 일부 내용이 다를 수 있습니다.

 

유연하지 못한 설계는 무엇이 문제일까

영화 예매 시스템을 만든다고 생각해 보자.

 

만약 'Movie' 클래스가 할인 정책을 직접 생성해서 적용한다면,

새로운 할인 방식이 생길 때마다 Movie 클래스를 수정해야 한다.

 

이런 방식은 새로운 요구사항이 발생할 때마다 기존 코드를 반복적으로 고쳐야 하므로 유연하지 못한 설계다.

 

[ 예제 코드 ]

public class Movie {
    
	private String title;
    private DiscountPolicy discountPolicy;

    public Movie(String title) {
        this.title = title;
        this.discountPolicy = new AmountDiscountPolicy(...); // 직접 생성
    }

    public Money calculateMovieFee(Screening screening) {
        return fee.minus(discountPolicy.getDiscountAmount(screening));
    }

}

이 설계는 할인 정책이 바뀌면 매번 Movie를 수정해야 하기 때문에,

개방-폐쇄 원칙(OCP)을 위반하게 된다.

 

생성과 사용 책임이 한 클래스에 섞여 있어 유지보수도 어렵다.

 

 

문제는 높은 결합도와 낮은 응집도

설계가 강하게 결합되면,

작은 변화 하나에도 여러 부분이 연쇄적으로 바뀐다.

 

상위 객체가 하위 객체의 구체적인 구현을 직접 알게 되면 코드 변경의 파급력이 커진다.
또한 내부에서 객체를 직접 생성하면 숨겨진 의존성이 생겨 디버깅과 테스트가 어려워진다.

 

즉, 문제는 객체 간의 결합이 높고 각 클래스가 명확한 책임에 집중하지 못한다는 것이다.

 

 

유연한 설계를 위한 리팩토링

이 문제를 해결하려면 객체의 생성 책임을 외부로 분리하고,

추상화에 의존하도록 코드를 바꿔야 한다.

 

할인 정책을 외부에서 주입받게 하면,

영화(Move)는 구체적인 할인 정책과 무관해진다.

 

[ 예제 코드 ]

public class Movie {
    
	private String title;
    private DiscountPolicy discountPolicy;

    public Movie(String title, DiscountPolicy discountPolicy) {
        this.title = title;
        this.discountPolicy = discountPolicy;
    }

    public Money calculateMovieFee(Screening screening) {
        return fee.minus(discountPolicy.getDiscountAmount(screening));
    }

}

이렇게 의존성을 외부에서 주입하면

새로운 할인 정책을 추가할 때도 기존 코드를 변경하지 않아도 된다.

 

이것이 바로 개방-폐쇄 원칙(OCP)이 말하는

"확장에는 열려있고, 수정에는 닫혀 있는" 상태다.

 

 

유연한 설계를 위한 원칙

유연한 설계를 이루려면 다음과 같은 원칙을 기억해야 한다:

 

  • 개방-폐쇄 원칙(OCP)
    변경은 새로운 코드의 추가로 이루어지고 기존 코드 수정은 최소화한다.
  • 생성과 사용의 분리
    객체를 생성하는 책임을 외부로 넘겨 객체 간의 결합도를 낮춘다.
  • 의존성 주입 (DI)
    상위 클래스와 하위 클래스 모두 추상화에만 의존하게 하여 변화의 영향을 줄인다.
  • 정보 전문가와 가공물 패턴
    도메인 객체가 적합하지 않다면 기술적인 책임을 맡는 객체를 추가해 유연성을 확보한다.

 

 

마무리 - 객체 협력으로 만들어가는 유연함

유연한 설계는 단지 이론적 이상이 아니라 실무적인 요구사항이다.


너무 극닥적으로 유연성을 추구하면 코드가 복잡해질 수 있지만,

적절한 추상화와 책임 분리를 통해 변화에 강한 코드를 만들 수 있다.

 

레고 블록을 떠올려보자.

다양한 블록을 만들어두면 여러 형태로 조립할 수 있지만,

설계 단계에서 신중한 고민이 필요하다. 소프트웨어 설계도 마찬가지다.

 

객체가 자신에게 맡겨진 역할에만 집중하도록 책임을 분산하고 협력을 설계하면,

변화가 생겨도 유연하게 대응할 수 있다.

 

즉, 유연한 설계는 "어떤 객체가 어떤 책임을 질 것인가"를 끊임없이 고민하는 데서 출발한다.

 

블로그의 정보

코드의 여백

rowing0328

활동하기