Pet: 추상 클래스, Dog/Cat: 상속받은 자손 클래스
객체지향에서 가장 큰 특성 중 하나인 '다형성'을 통해 Pet 참조변수로 Dog, Cat 인스턴스를 참조할 수 있다. 그리고 'cry()'라는 공통된 인터페이스를 사용할 수 있다.
cf) 메서드 오버라이딩
상속받은 메서드는 자손 클래스에 표시하지 않음.
하지만 display() 경우 메서드 오버라이딩을 한 것이므로 추상 메서드 표시가 아닌 것으로 자손 클래스에 표기.
그렇다고 무작정 상위 분류(Duck)에 속하는 것들을 파생 클래스(MallarDuck, RedHeadDuck, ..)로 선언하면 문제가 발생할 수 있다. fly()라는 상위 클래스에서 정의된 인터페이스가 의미가 없는 파생 클래스가 있기 때문이다. 바로 못나는 오리(RubberDuck)가 그 예이다.
이를 어떻게 해결할 수 있을 까?
날 수 있는 오리들에 해당하는 파생 클래스들에서 fly() 메서드(기능)을 추가하는 것이다. 하지만 이는 클라이언트 코드에서 추상 클래스를 의존하고 있는 상황(DIP)에서 fly 기능을 활용하지 못하는 문제가 발생할 수 있다. 결국 구체 클래스에 의존해야만 하는 상황이 발생할 수 있다.
추상 클래스로 선언하고, RubberDuck의 경우 fly()를 날지 못하는 형태로 오버라이딩할 수 있다.
하지만 '날지 못하는' 종류의 오리가 추가될 때마다 날지 못하는 방식으로의 추가 구현을 계속 한다는 것은 귀찮은 일이 될 수 있다.
상위 클래스에서 일반 메서드로 선언하고, 날지 못하는 오리 클래스에서 (날지 못하는 방식으로)오버라이딩할 수 있다.
하지만 이 또한 '방법 2'와 마찬가지로 '날지 못하는' 종류의 오리가 추가될 때마다 추가 구현이 필요하다.
전략적으로 따로 '나는(fly) 기능'을 독립적으로 빼 놓을 수 있다. 이것이 '전략 패턴(strategy pattern)'이다.
'정렬 기능' 예시
만약 돈을 내야한다면 sortingMethod 타입 변수(참조 변수)에 QuickSort 인스턴스 생성하고,
돈을 내도 되지 않다면 '' BubbleSort 인스턴스 생성한다.
이 후 sort() 메서드에서 독립시킨 기능의 메서드를 참조 변수를 통해 호출한다.