Object

[Object] Chapter04. 설계 품질과 트레이드오프

개발자 문문 2025. 7. 26. 20:15

안녕하세요 개발자 문문입니다.

오늘은 설계 품질과 트레이드오프에 대해 공부해보겠습니다.

 

이전 챕터들에서 공부한 역할, 책임, 협력의 정의입니다.

- 협력 : 애플리케이션의 기능을 구현하기 위해 메시지를 주고받는 객체들 사이의 상호작용

- 책임 : 객체가 다른 객체와 협력하기 위해 수행하는 행동

- 역할 : 대체 가능한 책임의 집합

이중 가장 중요한 것은 책임으로 책임이 객체지향 애플리케이션 전체의 품질을 결정합니다.

 

객체지향 설계란 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동을 의미합니다.

결합도와 응집도를 합리적인 수준으로 유지하려면 객체의 상태가 아닌 객체의 행동에 초점을 맞춰야합니다.

- 객체의 상태 : 객체가 저장해야하는 데이터의 집합

 

객체지향 설계에서 시스템을 객체로 분할

1. 상태를 분할의 중심축으로 지정 (데이터 중심의 관점)

- 이 관점에서의 객체는 자신이 포함하고 있는 데이터를 조작하는 데 필요한 오퍼레이션을 정의 하는 것

- 객체는 독립된 덩어리

2. 책임을 분할의 중심축으로 지정 (책임 중심의 관점)

- 이 관점에서의 객체는 다른 객체가 요청할 수 있는 오퍼레이션을 위해 필요한 상태를 보관

- 객체는 협력하는 공동체의 일원

 

시스템을 분할하려면 어떤 관점이 좋을까요?

훌륭한 객체지향 설계는 책임에 초점을 맞추는 것 입니다.

- 객체의 상태는 구현에 속합니다.

- 데이터 중심의 관점에서는 구현에 관한 세부사항이 객체의 인터페이스에 스며들게 되어 캡슐화의 원칙이 무너집니다.

- 상태 변경이 되면 인터페이스가 변경이 되어야 하기 때문에 데이터 중심 관점의 설계는 변경에 취약합니다.

- 객체의 책임은 인터페이스에 속합니다.

- 책임 중심의 관점에서 객체는 책임을 드러내는 안정적인 인터페이스 뒤로 책임을 수행하는 데 필요한 상태를 캡슐화함으로써 구현 변경에 대한 파장이 외부로 퍼져나가는 것을 방지합니다. 

- 즉, 책임 중심의 관점의 설계가 변경에 안정적인 설계가 가능합니다.

 

설계 트레이드오프

데이터 중심 설계의 문제점을 살펴보겠습니다.

[캡슐화]

- 외부에서 알 필요가 없는 부분을 감춤으로써 대상을 단순화하는 추상화의 한 종류

- 객체 지향의 가장 중요한 원리는 불안정한 구현 세부사항을 안정적인 인터페이스 뒤로 캡슐화 하는 것

- 데이터 중심의 설계는 캡슐화를 위반하고 객체의 내부 구현을 인터페이스의 일부로 만듭니다.

- 책임 중심의 설계객체의 내부 구현을 안정적인 인터페이스 뒤로 캡슐화합니다.

public class Movie {
	private Money fee;

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

- 이 클래스는 캡슐화의 원칙을 위반합니다.

- getFee , setFee 메서드가 Money 타입에 fee라는 인스턴스 변수가 존재한다는 사실을 드러냅니다.

- 객체가 수행할 책임이 아니라 내부에 저장할 데이터에 초점을 맞췄기 때문에 캡슐화의 원칙을 위반한 것 입니다.

 

[응집도]

- 모듈에 포함된 내부 요소들이 연관돼 있는 정도

- 객체지향 관점에서 응집도는 객체 또는 클래스에 얼마나 관련 높은 책임들을 할당했는지를 나타냄

- 요구사항이 변경되었을 때 하나 이상의 클래스를 수정해야 한다면 이는 응집도가 낮다는 것 입니다.

 

 

[결합도]

- 다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타내는 척도

- 객체지향 관점에서 결합도는 객체 또는 클래스가 협력에 필요한 적절한 수준의 관계만을 유지하고 있는지를 나타냄

public class ReservationAgency {

	public Reservation reserve(Customer customer, Screening screening, int audienceCount)
	{
		...
        
		Money fee;
		
		if(discountable) {
			...
			}
			
			fee = movie.getFee().minus(discountAmount);
		} else {
			fee = movie.getFee();
		}
		
		...

- 여기서 fee의 타입을 변경한다고 가정해보겠습니다.

- 이를 위해서는 getFee의 메서드 반환 타입도 함께 수정해야 합니다.

- ReservationAgency의 내부 구현도 변경된 타입에 맞게 수정해야 합니다.

- 이런 경우를 강하게 결합되었다고 합니다.

 

"데이터 중심의 설계는 객체의 행동보다는 상태에 초점을 맞춘다"

- 설계 시작시 "이 객체가 포함해야 하는 데이터는 무엇인가?" 로 시작을 하게되면 너무 이른 시기에 내부 구현에 초점을 맞추게 되고 그만큼 변경에 취약하게 됩니다.

- 과도한 접근자(getter), 수정자(setter)의 사용은 인스턴스 변수를 public으로 선언한 것과 다르지 않습니다.

- 데이터를 먼저 결정하고 데이터를 처리하는 데 필요한 오퍼레이션을 나중에 결정하는 방식은 데이터에 관한 지식이 객체의 인터페이스에 고스란히 드러나게 되기 때문에 캡슐화를 위반하고 변경에 취약해지는 것 입니다.

 

"데이터 중심의 설계는 객체를 고립시킨 채 오퍼레이션을 정의하게 만든다"

- 올바른 객체지향의 무게 중심은 항상 객체의 내부가 아니라 외부에 맞춰져 있어야 합니다.

- 데이터 중심의 설계의 초점은 객체의 외부가 아니라 내부로 향합니다.

- 객체의 인터페이스에 구현이 노출되어 있기 때문에 협력이 구현 세부사항에 종속되어 있었고 그에 따라 객체 내부 구현이 변경됐을때 협력하는 객체 모두가 영향을 받을 수 밖게 없습니다.