오브젝트 4장 객체지향 프로그래밍

장현진·2022년 8월 5일
0

Ch.4 설계 품질과 트레이드 오프

설계는 변경을 위해 존재하고 변경에는 어떤 식으로든 비용이 발생한다.

훌륭한 설계란 합리적인 비용안에서 변경을 수용할 수 있는 구조를 만드는 것

객체의 상태는 구현에 속한다. 구현은 불안정하기 때문에 변하기 쉽다.

상태를 객체 분할의 중심축으로 삼으면 구현에 관한 세부사항이 인터페이스에 스며들게 되어 결과적으로 상태 변경은 인터페이스의 변경을 초래한다. 즉, 인터페이스에 의존하는 모든 객체에게 변경의 영향이 퍼지게 된다.
그러나, 객체의 책임은 인터페이스에 속한다. 따라서 책임에 초점을 맞추면 상대적으로 변경에 안정적인 설계를 얻을 수 있다.

캡슐화

구현 : 나중에 변경될 가능성이 높은 어떤 것
인터페이스 : 상대적으로 안정적인 부분
구현을 내부에 숨기고 외부에는 상대적으로 안정적인 부분만 공개함으로써 변경의 여파를 통제할 수 있다.

캡슐화란 변경 가능성이 높은 부분을 객체 내부로 숨기는 추상화 기법이다.



가짜 캡슐화

가짜 캡슐화 1

public class Movie {
    private Money fee;
    
    public Money getFee() {
        return this.fee;
    }
    
    public void setFee(Money fee) {
        this.fee = fee;
    } 
}

위의 예제는 내부의 구현(상태)를 외부로 노출시키지 않았으며, 접근자, 수정자를 통해서 내부의 구현에 접근하도록 하고 있습니다. 그렇다면 위의 예제는 진정한 캡슐화일까요?? 아쉽지만, 아닙니다 외부에서 fee의 존재를 알도록 했기 때문입니다. 만약 fee의 타입이 변경된다면, 이 접근자와 수정자를 이용하는 모든 객체가 수정이 필요하게 됩니다. 위와 같이 내부 속성을 외부로부터 감추는 것은 데이터 캡슐화로 캡슐화의 한 종류일 뿐입니다.

가짜 캡슐화 2

public class DiscountCondition {
    private DiscountConditionType type;
    private int sequence;
    private DayOfWeek dayOfWeek;
    private LocalTime startTime;
    private LocalTime endTime;
    
    public DiscountConditionType getType() { ... }
    
    public boolean isDiscountable(DayOfWeek dayOfWeek, LocalTime time) { ... }
    
    public boolean isDiscountable(int sequence) { ... }    
}

위의 isDiscountable() 메소드들은 DiscountCondition 객체가 가지고있는 상태와 비교하여, 할인 조건에 부합하는지를 판단하고 할인 여부를 반환하는 메소드들입니다. 위의 예제는 캡슐화가 잘 되었다고 착각을 하기에 충분해 보입니다. 하지만 이역시도 캡슐화가 제대로 이루어지지 않은 예입니다.

isDiscountable() 메소드들의 시그니쳐를 보면 파라미터를 통해 외부에 내부의 구현을 노출하고 있음을 알 수 있습니다. 이 상황에서 DiscountCondition의 내부 속성들을 변경한다면 isDiscountable() 을 사용하는 외부의 객체들 역시 변경이 필요할 것입니다. 내부 구현의 변경을 통해 외부의 변경이 발생한다면 캡슐화가 부족하다는 증거입니다.

가짜 캡슐화 3

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private List<DiscountCondition> discountConditions;
    
    private MovieType movieType;
    private Money discountAmount;
    private double discountPercent;
    
    public MovieType getMovieType() { ... }
    public Money calculateAmountDiscountedFee() { ... }
    public Money calculatePercentDiscountedFee() { ... }
    public Money calculateNoneDiscountedFee() { ... }
}

위의 예제는 어떨까요? 메소드의 파라미터나 반환값으로 내부에 포함된 어떠한 속성도 나타내지 않고 있습니다. 아쉽게도 위의 예제 역시 캡슐화가 잘 되지 않은 예입니다.

위 예제역시 내부의 구현을 인터페이스에 노출시키고 있는데요, 바로 할인정책의 종류입니다. 만약 할인정책이 추가되거나 제거가 된다면 어떨까요?? 따라서 할인정책 세가지를 포함하고 있다는 내부구현을 외부에 노출함으로써 캡슐화가 제대로 이루어지지 않았음을 알 수 있습니다.

Getter 와 Setter

Getter 와 Setter 공개해야할 범위를 제공한다. 메소드자체의 사용금지를 뜻하지는 않는다.
멤버변수가 객체인경우 Getter 객체내부에서 재정의하여 제공한다.

데이터 중심 설계의 문제점

너무 이른 시기에 데이터에 관해 결정하도록 강요한다.
협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 결정한다.
=> 낮은 응집도와 높은 결합도를 갖는다.
=> 변경에 취약하다.

올바른 객체지향 설계의 무게 중심은 항상 객체의 내부가 아니라 외부에 맞춰져야 한다.
데이터 중심 설계에서 초점은 객체의 외부가 아닌 내부로 향한다.

Ex) 어떤 데이터를 선택할지, 데이터로 어떤 역할을 수행할 지 책임을 고려하지 않고 고립해서 생각한다.
객체의 인터페이스에 구현이 노출돼 있기 때문에 객체지향의 1 원칙인 캡슐화를 위반한다.

0개의 댓글

관련 채용 정보