부록 A. 계약에 의한 설계
- 인터페이스만으로는 객체의 행동에 관한 다양한 관점을 전달하기 어렵습니다.
- 명령의 부수효과를 쉽고 명확하게 표현할 수 있는 커뮤니케이션 수단이 필요하며, 이때 계약에 의한 설계(Design By Contract, DBC) 가 주는 혜택을 보게 됩니다.
#
01. 협력과 계약#
부수효과를 명시적으로- 계약에 의한 설계를 사용하면 제약 조건을 명시적으로 표현하고 자동으로 문서화할 수 있을뿐만 아니라 실행을 통해 검증할 수 있습니다.
#
계약- 계약에 대한 세부조건은 아래와 같은 특성을 가집니다.
- 각 계약 당사자는 계약으로부터 이익(benefit) 을 기대하고 이익을 얻기 위해 의무(obligation) 을 이행합니다.
- 각 계약 당사자의 이익과 의무는 계약서에 문서화됩니다.
- 계약은 협력을 명확하게 정의하고 커뮤니케이션할 수 있는 범용적인 아이디어입니다.
#
02. 계약에 의한 설계- 계약은 협력에 참여하는 두 객체의 사이의 의무와 이익을 문서화한 것 입니다.
- 각 계약 당사자는 계약으로부터 이익(benefit) 을 기대하고 이익을 얻기 위해 의무(obligation) 을 이행합니다.
- 각 계약 당사자의 이익과 의무는 계약서에 문서화됩니다.
- 계약에 의한 설계 개념은 "인터페이스에 대해 프로그래밍하라"는 원칙을 확장한 것입니다.
- 일반적으로 세 가지 기대가 바로 계약에 의한 설계를 구성하는 세 가지 요소에 대응됩니다.
- 사전조건(precondition) : 메서드가 호출되기 위해 만족돼야 하는 조건
- 사후조건(postcondition) : 메서드가 실행된 후에 클라이언트에게 보장해야 하는 조건
- 불변식(invariant) : 항상 참이라고 보장되는 서버의 조건
#
사전조건- 사전조건을 만족시키지 못해서 메서드가 실행되지 않을 경우 클라이언트에 버그가 있다는 것을 의미합니다.
- 일반적으로 사전조건은 메서드에 전달된 인자의 정합성을 체크하기 위해 사용됩니다.
#
사후조건- 일반적으로 사후조건은 다음의 세 가지 용도로 사용됩니다.
- 인스턴스 변수의 상태가 올바른지를 서술하기 위해
- 메서드에 전달된 파라미터의 값이 올바르게 변경됐는지를 서술하기 위해
- 반환값이 올바른지를 서술하기 위해
- 아래의 두 가지 이유로 인해 사전조건보다 어려울 수 있습니다.
- 한 메서드 안에서 return 문이 여러 번 나올 경우
- 실행 전과 실행 후의 값이 비교해야 하는 경우
#
불변식- 불변식은 다음과 같은 두가지 특성을 가집니다.
- 불변식은 클래스의 모든 인스턴스가 생성된 후에 만족돼야 합니다.
- 불변식은 클라이언트에 의해 호출 가능한 모든 메서드에 의해 준수돼야 합니다.
#
03. 계약에 의한 설계와 서브타이핑- 서브타입이 리스코프 치환 원칙을 만족시키기 위해서는 클라이언트와 슈퍼타입 간에 체결된 계약을 준수해야 합니다.
- 리스코프 치환 원칙의 규칙을 두 가지 종류로 세분화할 수 있습니다.
- 첫 번째 규칙은 협력에 참여하는 객체에 대한 기대를 표현하는 계약 규칙
- 두 번째 규칙은 교체 가능한 타입과 관련된 가변성 규칙
- 계약 규칙(contract rules)은 슈퍼타입과 서브타입 사이의 사전조건, 사후조건, 불변식에 대한 서술할 수 있는 제약에 관한 규칙
- 서브타입에 더 강력한 사전조건을 정의할 수 없습니다.
- 서브타입에 더 완화된 사후조건을 정의할 수 없습니다.
- 슈퍼타입의 불변식은 서브타입에서도 반드시 유지돼야 합니다.
- 가변성 규칙(varinace rules) 은 파라미터와 리턴 타입의 변형과 관련된 규칙입니다.
- 서브타입의 메서드 파라미터는 반공변성을 가져야 합니다.
- 서브타입의 리턴 타입은 공변성을 가져야 합니다.
- 서브타입은 슈퍼타입이 발생시키는 예외와 다른 타입의 예외를 발생시켜서는 안됩니다.
#
계약 규칙- 서브타입에 더 강력한 사전조건을 정의할 수 없습니다.
- 사전조건을 보장해야 하는 책임은 클라이언트에게 있습니다.
- 서브타입에 더 완화된 사후조건을 정의할 수 없습니다.
- 사후조건 완화는 리스코프 치환 원칙 위반입니다.
- 사후조건 강화는 계약에 영향을 미치지 않습니다.
- 일찍 실패하기 > 문제가 발생한 곳에서 프로그램이 실패하도록 만드는 것이 좋습니다.
- 슈퍼타입의 불변식은 서브타입에서도 반드시 유지돼야 합니다.
- 계약 관점에서 캡슐화는 중요합니다. (변경 측면에서)
#
가변성 규칙- 서브타입은 슈퍼타입이 발생시키는 예외와 다른 타입의 예외를 발생시켜서는 안됩니다.
- 클라이언트의 관점에서 자식클래스가 부모 클래스가 하는 일보다 더 적은 일을 수행한다는 공통점이 있습니다.
- 서브타입의 리턴 타입은 공변성을 가져야 합니다.
- 공변성에 대한 정의
- 공변성 : S와 T 사이의 서브타입 관계가 그대로 유지됩니다.
- 반공변성 : S와 T 사이의 서브타입 관계가 역전됩니다.
- 무공변성 : S와 T 사이에는 아무런 관계도 존재하지 않습니다.
- 부모 클래스에서 구현된 메서드를 자식 클래스에서 오버라이딩할 때 부모 클래스에서 선언한 반환타입의 서브타입으로 지정할 수 있는 특성을 리턴 타입 공변성(return type convariance) 라고 부릅니다.
- 공변성에 대한 정의
- 서브타입의 메서드 파라미터는 반공변성을 가져야 합니다.
- 부모 클래스에서 구현된 메서드를 자식 클래스에서 오버라이딩할 때 파라미터 타입을 부모 클래스에서 사용한 파라미터의 슈퍼타입으로 지정할 수 있는 특성을 파라미터 타입 반공변성(parameter type contravariance) 라고 부릅니다.
#
함수 타입과 서브타이핑- 최근의 객체지향 언어들은 이름 없는 메서드를 정의할 수 있게 허용합니다.
- 익명함수, 함수 리터럴, 람다 표현식 등.