it 책/오브젝트: 코드로 이해하는 객체지향 설계

계약에 의한 설계

lgvv 2024. 11. 26. 00:06

계약에 의한 설계

 

느낀점

부록 A인데 계약에 의한 설계를 읽어보면서 프레임워크나 라이브러리를 내부를 어떻게 구현하고, 외부에 어떤 인터페이스들을 어떻게 제공하면 좋은지 더 생각하는 부록이었음.

 

 

계약에 의한 설계

인터페이스를 다듬고 명령과 쿼리를 분리했다고 하더라도 명령으로 인해 발생하는 부수효과를 명확하게 표현하는 데는 한계가 존재

  • 주석으로 부수효과를 설명하는 것도 가능하겠으나, 파급효과를 명확하게 전달하기가 쉽지 않을 뿐더러 시간이 흐를수록 구현을 정확하게 반영하지 못할 가능성도 높음
  • 메서드의 구현이 단순하다면 부수효과를 쉽게 이해할 수 있을지도 모르지만, 부수효과를 가진 다수의 메서드들을 연이어 호출하는 코드를 분석하는 경우에는 실행결과를 예측하기 어려울 수 있음
  • 명령의 부수효과를 쉽고 명확하게 표현할 수 있는 커뮤니케이션 수단.

 

부수효과를 명시적으로

객체지향의 핵심은 객체들이 수행하는 행동

  • 안타깝게도 프로그래밍 언어로 작성된 인터페이스는 객체가 수신할 수 있는 메시지는 정의할 수 있지만 객체 사이의 의사소통 방식은 명확하게 정의할 수 없음
  • 명령과 쿼리를 분리했기 때문에 조건을 만족시키는지 여부를 확인한 후 reschdule 메서드를 호출해야 함.

 

class Event { 
	public func isSatisfied(recurringSchedule: Schedule) -> Bool { 
    	/* */
	}
    
    public func reschdule(recurringSchedule: Schedule) { 
    	Contract.Requires(isSatisfied(recurringSchedule: recurringSchedule))
    }
}

 

일반적인 정합성 체크 로직은 코드의 구현 내부에 숨겨져 있어 실제로 코드를 분석하지 않는 한 정확하게 파악하기 쉽지 않음.

  • 명시적으로 표현하고 자동으로 문서화할 수 있을뿐만 아니라 실행을 통해 검증할 수도 있음

 

계약

계약의 내용은 세부적인 상황에 따라 다르나 다음과 같은 특성을 지님

  • 각 당사자는 이익을 기대하고, 의무를 이행함
  • 의무는 계약서에 문서화

의도를 드러내는 인터페이스를 만들면 오퍼레이션의 시그니처만으로도 어느 정도까지는 명시할 수 있으며, 서버는 자신이 처리할 수 있는 범위의 값들을 클라이언트가 전달할 것이라고 기대

  • 사전조건: 메서드가 호출되기 위해 만족돼야 하는 조건, 메서드의 요구사항을 명시
  • 사후조건: 실행된 후에 클라이언트에게 보장해야 하는 조건
  • 불변성: 항상 참이라고 보장되는 서버의 조건. 메서드가 실행되는 도중에는 불변식을 만족시키지 못할 수도 있지만 메서드를 실행하기 전이나 종료된 후 불변식은 항상 참이어야 함.


계약에 의한 설계와 서브타이핑

클라이언트가 만족시켜야 하는 사전조건과 클라이언트의 관점에서 서버가 만족시켜야 하는 사후 조건을 기술

  • 계약 규칙: 협력에 참여하는 객체에 대한 표현
    • 공변성과 반공변성이 중요해지는 곳은 상속이 제네릭 프로그래밍과 만나는 지점
  • 가변성 규칙: 교체 가능한 타입과 관련

일찍 실패하기(Fail Fast)

  • 의아하게 생각할 수도 있지만, 마이너스 금액을 그대로 사용하는 것보다 종료하는 것이 올바른 선택
  • 지금의 편안함을 위해서 오류를 감춰서는 안됨
  • 차라리 문제가 발생한 그 위치에서 프로그램이 실패하도록 만들며, 문제의 원인을 파악할 수 있는 가장 빠른 방법은 문제가 발생하자마자 프로그램이 일찍 실패하게 만드는게 나음

서브타입의 리턴 타입은 공변성을 가져야 함

  • 공변성(covariance): S와 T 사이의 서브타입 관계가 그대로 유지. 해당 위치에서 서브타입인 S가 슈퍼타입인 T 대신 사용될 수 있음 흔이 이야기하는 리스코프치환 원칙은 공변성과 관련된 원칙이라고 생각
  • 반공변성(contravariance): S와 T 사이의 서브타입 관계 역전, 이 경우 해당 위치에서 슈퍼타입인 T가 서브타입인 S 대신 사용될 수 있음
  • 무공변성(invariance): S와 T 사이에는 아무런 관계도 존재하지 않음.
  • 부모 클래스에서 구현된 메서드를 자식 클래스에서 오버라이딩할 때 부모 클래스에서 선언한 변환타입으로 지정할 수 있는 특성을 리턴 타입 공변성이라고 함.