직사각형 API의 하위 타입이 높이를 바꾸자 너비도 함께 바뀝니다. 왜 치환에 실패한 걸까요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
리스코프 치환 원칙은 하위 타입을 부모 타입 대신 넣어도 부모를 사용하던 코드의 계약이 깨지지 않아야 한다는 뜻입니다. 여기서 계약은 메서드 이름과 타입만이 아니라 허용하는 입력, 보장하는 결과, 예외와 상태 변화까지 포함합니다. 직사각형의 너비와 높이를 독립적으로 바꿀 수 있다는 부모 계약을 클라이언트가 기대한다면, 너비를 바꿀 때 높이까지 바꾸는 정사각형 구현은 그 기대를 깨뜨립니다.
계약 관점에서 하위 타입은 부모보다 더 강한 사전조건, 즉 입력 제한을 요구해서는 안 됩니다. 반대로 부모가 약속한 결과보다 약한 사후조건을 내놓아서도 안 됩니다. 부모가 음수가 아닌 크기를 받는다고 했는데 자식이 양수만 받는다면 기존 호출자가 실패할 수 있고, 부모가 호출 후 목록이 정렬된다고 약속했는데 자식이 정렬을 보장하지 않으면 치환이 불가능합니다. 예외의 종류나 호출 횟수처럼 관찰 가능한 동작도 계약에 포함될 수 있습니다.
이 반례는 정사각형이 수학적으로 직사각형의 한 종류인지보다, 변경 API의 행동 계약이 호환되는지가 문제라는 점을 보여 줍니다. 독립 변경이 필요한 타입과 한 변만 바꾸는 타입을 같은 인터페이스에 억지로 넣기보다 읽기 전용 도형과 별도 크기 조정 역할로 나누겠습니다. 상속을 고칠 수 없다면 부모 타입이 실제로 보장하는 조건을 명시하고, 그 조건을 하위 타입별 계약 테스트로 검증하겠습니다.
치환은 관찰 가능한 계약입니다
부모 직사각형이 setWidth와 setHeight를 독립적으로 바꿀 수 있다고 약속하면, 정사각형 하위 타입이 높이를 바꾸며 너비까지 바꾸는 순간 클라이언트의 사후 상태 기대를 깨뜨립니다. 부모보다 더 좁은 입력만 받거나, 부모가 보장한 결과·예외·호출 횟수를 약화하는 것도 같은 문제입니다. 컴파일되는 반환 타입과 is-a 분류만으로 행동 호환성을 판단하지 않고, 입력·상태·외부 효과를 포함한 계약을 실행해 보겠습니다.
역할을 작게 다시 나눕니다
독립 크기 변경이 필요한 클라이언트, 비율 유지가 필요한 클라이언트, 읽기만 필요한 클라이언트를 하나의 변경 인터페이스에 넣지 않습니다. 읽기 전용 도형과 크기 조정 역할을 분리하거나 조합으로 연결하고, 상속을 유지해야 한다면 부모의 모든 유효 입력을 하위 타입에 넣는 계약 테스트를 둡니다. 자식이 예외를 더 자주 내거나 호출 후 상태를 다르게 만들지 확인하며, 타입 계층보다 실제 클라이언트가 요구하는 최소 행위를 기준으로 설계를 바꾸겠습니다.
구체적인 경계 사례
구체적으로 너비를 5, 높이를 4로 바꾼 뒤 면적이 20이라고 기대하는 클라이언트가 있습니다. 정사각형 setter가 두 변을 함께 바꾸면 마지막 높이 설정 뒤 면적은 16이 됩니다. 두 setter가 실행되고 타입 검사도 통과하지만 부모의 사후조건을 만족하지 못합니다. 읽기 전용 면적 역할로 좁히면 이 특정 변경 계약의 충돌은 없어질 수 있습니다.
성능 차이가 항상 치환 위반인 것은 아닙니다. 부모가 지연 상한이나 비블로킹을 계약으로 약속한 경우에만 자식의 느린 대기가 그 계약을 깨뜨립니다. 모든 구현 세부사항을 부모 계약으로 고정하면 대안 구현이 불가능해지므로 클라이언트가 의존해도 되는 보장을 명시합니다. 예상하지 못한 예외, 입력 제한, 객체 상태 변화는 그 명시된 보장과 대조해 판정하겠습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- 사전조건·사후조건·예외·상태·부수효과를 포함한 행동 계약으로 치환을 판정한다.
- 직사각형 반례를 클라이언트 기대와 연결해 설명한다.
- 최소 역할 인터페이스와 하위 타입 계약 테스트로 설계를 수정한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- is-a 관계나 같은 반환 타입만으로 치환 가능하다고 판단한다.
- 하위 타입의 더 강한 입력 조건과 약한 결과를 구현 차이로 넘긴다.
- 호출 후 상태·예외·외부 효과를 부모 계약에서 제외한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- 반공변 입력과 공변 반환의 타입 규칙을 지키면 행동 치환도 보장되나요? 사전·사후조건은 어떻게 따로 검사하나요?
- 부모 계약에 명시되지 않은 암묵적 동작을 발견하면 문서와 테스트를 어떻게 보완할까요?
- 상속 대신 인터페이스 분리로 바꿀 때 클라이언트별 최소 계약을 어떻게 도출할까요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.