/** * 할인 정책 */ publicabstractclassDiscountPolicy { // DiscountPolicy의 인스턴스 생성이 필요없어 추상 클래스로 구성 // 하나의 할인 정책은 여러 개의 할인 조건을 포함할 수 있음 private List<DiscountCondition> conditions = newArrayList<>();
// 생성자의 파라미터 목록을 이용해 초기화에 필요한 정보를 전달하도록 강제 Movieavator=newMovie("아바타", Duration.ofMinutes(120), Money.wons(10000), newAmountDiscountPolicy(Money.wons(8000), newSequenceCondition(1), newSequenceCondition(10), newPeriodCondition(DayOfWeek.MONDAY, LocalTime.of(10, 0), LocalTime.of(11, 59)), newPeriodCondition(DayOfWeek.TUESDAY, LocalTime.of(10,0), LocalTime.of(20, 59))));
Movie의 인스턴스는 실행 시에 AmountDiscountPolicy나 PrecentDiscountPolicy의 인스턴스에 의존해야 함
코드 수준에서 Movie 클래스는 이 두 클래스 중 어떤 것에도 의존하지 않음
오직 추상 클래스 DiscountPolicy에만 의존하고 있음
1 2 3 4 5
// 실행 시에 Movie 는 AmountDiscountPolicy 에 의존 Movieavator=newMovie("아바타", Duration.ofMinutes(120), Money.wons(10000), newAmountDiscountPolicy(Money.wons(8000), ... ));
1 2 3 4 5
// 비율 할인 정책 적용 시 PercentDiscountPolicy를 전달하기만 하면 됨 Movieavator=newMovie("아바타", Duration.ofMinutes(120), Money.wons(10000), newPercentDiscountPolicy(0.1, ... ));
코드의 의존성과 실행 시점의 의존성이 서로 다를 수 있다는 것이다. 다시 말해 클래스 사이의 의존성과 객체 사이의 의존성은 동일하지 않을 수 있다. 그리고 유연하고, 쉽게 재사용할 수 있으며, 확장 가능한 객체지향 설계가 가지는 특징은 코드의 의존성과 실행 시점의 의존성이 다르다는 것이다.
코드의 의존성과 실행 시점의 의존성이 다르면 다를 수록
코드를 이해하기 어려워 짐
디버깅 하기 점점 더 어려워짐
코드는 더 유연해지고 확장 가능해짐
재사용성이 높아짐
훌륭한 객체 지향 설계자로 성장하기 위해서는 항상 유연성과 가독성 사이에서 고민해야 한다.
차이에 의한 프로그래밍
상속은 기존 클래스를 기반으로 새로운 클래스를 쉽고 빠르게 추가할 수 있는 간편한 방법을 제공
부모 클래스와 다른 부분만을 추가하여 새로운 클래스를 쉽고 빠르게 만드는 방법을 “차이에 의한 프로그래밍” 이라고 부름
상속과 인터페이스
상속을 통해
부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받을 수 있음
자식 클래스는 자신의 인터페이스에 부모 클래스의 인터페이스를 포함하게 됨
결과적으로 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기때문에 외부 객체는 자식 클래스를 부모 클래스와 동일한 타입으로 간주 할 수 있음
1 2 3 4 5 6 7 8 9 10 11 12
@AllArgsConstructor publicclassMovie { /** * 요금 계산 * calculateDiscountAmount 에서 할인 요금 반환 * @param screening * @return */ public Money calculateMovieFee(Screening screening){ return fee.minus(discountPolicy.calculateDiscountAmount(screening)); } }
자식 클래스는 상속을 통해 부모 클래스의 인터페이스를 물려받기 때문에 부모 클래스 대신 사용될 수 있다. 컴파일러는 코드 상에서 부모 클래스가 나오는 모든 장소에 자식 클래스를 사용하는 것을 허용한다.
다형성
다형성은 컴파일 시간 의존성과 실행 시간 의존성을 다르게 만들 수 있는 객체지향의 특성을 이용해 서로 다른 메서드를 실행할 수 있게 함
Movie는 동일한 메시지를 전송하지만 실제로 어떤 메서드가 실행 될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라짐
다형성이란 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력을 말함
지연 바인딩(lazy binding) or 동적 바인딩(dynamic binding) : 메시지와 메서드를 실행 시점에 바인딩하는 것
초기 바인딩(early binding) or 정적 바인딩(static binding) : 전통적인 함수 호출 처럼 컴파일 시점에 실행될 함수나 프로시저를 결정하는 것
구현 상속과 인터페이스 상속
상속을 구현상속과 인터페이스 상속으로 나눌 수 있다.
구현 상속 : 서브 클래싱. 코드를 재사용하기 위한 목적으로 상속하는 것.
인터페이스 상속 : 서브타이핑. 부모 클래스와 자식 클래스가 인터페이스를 공유할 수 있도록 상속하는 것
상속은 구현상속이 아니라 인터페이스 상속을 위해 사용해야 한다.
인터페이스와 다형성
추상화와 유연성
추상화의 힘
위 다이어그램은 자식 클래스를 생샥한 코드 구조를 그림으로 표현한 것이다.
추상화를 사용할 경우 장점
추상화의 계층만 따로 떼어 놓고 살펴보면 요구사항의 정책을 높은 수준에서 서술할 수 있음
세부적인 내용은 무시한 채 상위 정책을 쉽고 간단히 표현
디자인 패턴이나 프레임워크 모두 추상화를 이용해 상위 정책을 정의함
추상화를 이용하면 설계가 좀더 유연해짐
기존 구조를 수정하지 않고도 새로운 기능을 쉽게 추가하고 확장 할 수 있음
유연한 설계
1 2 3 4 5 6 7 8 9 10
/** * 할인 정책이 없을 경우 */ publicclassNoneDiscountPolicyextendsDiscountPolicy{ @Override protected Money getDiscountAmount(Screening screening) { return Money.ZERO; } }