오브젝트 2.객체지향설계 프로그래밍

조현창·2022년 12월 18일
0

오브젝트

목록 보기
2/5

인터페이스

해당 인터페이스의 구현체에서 public이 아닌 메소드들은 자유롭게 수정이 가능하도록 명시하기 위해서 인터페이스에는 외부로 노출할 public 메소드들을 지정한다.

Template 패턴

커밋 링크
DiscountPolicy 추상클래스를 보면
실제 외부로 노출되는 메소드가 상속을 통해 구현해야하는 getDiscountAmount 추상메소드를 호출해 결정하는 것을 볼 수 있다.
이처럼 큰 흐름은 상위클래스에서 지정하고 상속받는 구현클래스에서 세부 내용에 해당하는 메소드를 재정의하는 방식을 Template패턴이라고 한다.
템플릿 메소드를 활용하여 제어의 역전을 구현할 수 있다. 관련링크

구현체 클래스 작성

커밋 링크
해당 방식을 사용하여 Screening클래스의 sequence 혹은 whenScreened를 참조하여
할인 여부를 검증할 수 있는 DiscountCondition 인터페이스와 isSatisfiedBy메소드를 작성한다.

이제 상영정보에 대하여 모든 DiscountCondition 중 해당되는 게 있다면 DiscountPolicy에서 자식클래스에 위임한 getDiscountAmount를 사용하여 각각의 할인정책을 구현한다.

그렇다면 각각의 구현 클래스의 존재를 모르는 Movie클래스는 어떻게 동작할 수 있을까?
생성자를 보면 DiscountPolicy 인자를 받게 되어 있는데 이 때 우리는
해당 인터페이스의 구현클래스 인스턴스 AmountPolicy를 초기화하여 전달하면 된다. 마찬가지로 AmountPoilicy에 PeriodCondition, SequenceCondition으로 구성된 DiscountCondition 인터페이스의 구현 클래스 인스턴스를 초기화하여 전달하면 런타임 시에 의존성을 체크하여 동작한다.

따라서 코드의 의존성은 상위 추상 클래스, 인터페이스에 있지만 런타임 시에는 각각의 구현클래스에 의존하게 된다.
또한 해당 구현체에는 인터페이스, 상위 추상 클래스의 public메소드가 있는 것이 보장되므로 컴파일 시 에러가 나지 않는다.

업캐스팅

위처럼 자식 클래스를 사용해서 상위 클래스, 인터페이스를 대체하는 것을 업캐스팅이라고 한다.

바인딩

컴파일 시점에 함수나 프로시저를 결정하는 것을 초기 바인딩, 정적 바인딩이라고 한다.
다형성 등 업캐스팅 처럼 런타임 시에 함수나 프로시저가 결정되는 것을 지연 바인딩, 동적 바인딩이라고 한다.

차이에 의한 프로그래밍

위 처럼 상속을 통해 중복되는 부분을 제거하고 간소화하는 방식을 차이에의한 프로그래밍이라고 한다.

외부 인터페이스를 수현하기 위해 자바나 C#은 인터페이스를 지원하지만 C++에서는 추상클래스로 해당 기능을 구현해야한다.

인터페이스 추가

커밋링크
만약 정책이 적용되지 않을 경우 caculateMovieFee가 동작하려면 어떻게 해야할까?
단순하게 예외처리를 할 수도 있을 것이다. 하지만 이러한 예외가 나올 때마다 Movie클래스를 수정해야한다면
확장은 열려있고 수정은 닫혀있는 객체지향의 목적을 흐리기 때문에
DiscountPolicy 추상 클래스를 상속하는 NoneDoscountPolicy 클래스를 추가할 것이다.
또한 명시적으로 인자로 전달할 수 있기 때문에 코드를 더 직관적으로 작성할 수 있다.
그러나 정책이 없는데 DiscountPolicy의 계산한다는 의미의 public메소드를 사용하는 것은 의미에 맞지 않을 수 있다.
getDiscountAmount에서 바로 0을 반환하는 것이 더 직관적이기 때문에
한번 더 getDiscountAmount 메소드를 가지는 인터페이스를 생성한다.
이후 NoneDiscountPolicy와 기존 DiscountPolicy를 해당 인터페이스를 상속하도록하고
Movie에서 기존 DiscountPolicy 추상 클래스가 아닌 해당 인터페이스를 합성하여 지연바인딩을 통해 사용할 수 있도록 변경한다.

캡슐화

지금까지 사용한 템플릿 패턴을 사용한 상속은 부모 클래스의 내용을 알아야한다.
또한 템플릿 패턴은 부모클래스의 구현이 자식 클래스에 노출돼야하므로 캡슐화가 어려워지고
부모클래스가 변경될 때 자식 클래스도 변경해야할 경우가 생긴다.

만약 Movie와 Policy를 상속으로 묶었다고 가정해보자
다른 Policy로 변경하기 위해서는 해당 클래스의 인스턴스를 생성하고 공통된 부분을 복사해야한다.
하지만 합성을 통해 필드로 존재하게 되면 해당 필드만 해당 정책의 인스턴스로 변경하면 되므로 훨씬 유연해진다.
갈아끼울 인스턴스를 생성해서 주입하는 경우 합성관계로 지정하고
차이에 의한 프로그래밍을 위해서는 상속관계로 지정한다.

부록

BigDecimal

double보다 정확하게 표기하기 위해서 만들어진 클래스
내부적으로 10진수로 저장되어 10진수로 표기할 때 무한한 정밀도를 보장한다.
초기화 시 new BifDecimal(double)을 하게되면 부정확한 정밀도로 적용되므로
내부적으로 toString으로 변환하는 BigDecimal.valueOf(double)을 사용하거나
new BigDecimal(String)으로 초기화해야한다.

precision, unscale:표기할 최대 숫자길이, 나머진 절삭한다. 0은 제외
scale, fraction: 맨 끝 0을 제외한 소숫점 자릿수, 0.0등 소숫점이 있으면 무조건 1로 친다.
최대 자리수는 최대2322^{32}이다.

MathContext

DECIMAL32: 7자리 제한 후 반올림
DECIMAL64: 16자리 제한 후 반올림
DECIMAL128: 34자리 제한 후 반올림
UNLIMITED: 제한 없으나 무한소수일 경우 예외반환

a.compareTo(b) b가 더 크면 1 작으면 -1 같으면 0을 반환
a.divide(BigDecimal, scale, RoundingMode) 나누기

RoundingMode

FLOOR: 내림
CEILING: 올림
HALF_EVEN: 반올림
a.remainder(BigDecimal, RoundingMode) 나머지 구하기

profile
공부중

0개의 댓글