알아보기 전, Polymorphism에 대해 알고 가면 좋다.

Polymorphism ?

우리 말로는 '다형성'이라고 하며, 똑같은 이름을 가졌지만 실제 수행 시엔 다른 결과를 내는 것을 말한다. 부모로부터 상속받은 메소드를 자식 단에서 오버라이딩했을 때를 그 중 하나의 예시로 들 수 있고 strategy pattern에서 이를 사용한다.

다음과 같은 관계를 갖는 아이들이 있다고 하자.

Dog와 Cat은 부모 Pet으로 받은 메소드 cry()를 각각 bark(), meow()를 호출하는 함수로 오버라이딩했다고 하자. 그리고 다음과 같이 자바 언어로 간단한 프로그램을 작성했다 하자.

public class PetMain(){
    public static void main(String[] args){
        Pet myPet;
        myPet = new Dog();
        myPet.cry();
        myPet = new Cat();
        myPet.cry();
    }
}

myPet의 cry()는 두 번 호출됐지만, 각각 다른 결과를 보일 것이다! 똑같은 이름을 가졌지만 실제 수행 시엔 다른 결과를 내는 것이며, 그 이유는 코드에서 드러나있듯이 첫 번째 cry를 할 땐 Dog인스턴스, 두 번째 cry를 할 땐 Cat인스턴스에 관한 것이기 때문. 

 

 

strategy pattern

결론부터 말하면 메소드, 알고리즘 등(객체들이 할 수 있는 '행위'라고 생각하면 됨)을 캡슐화하여 행위를 유연하게 확장하는 패턴을 말한다. 

 

다음과 같은 관계를 갖는 아이들이 있다고 하자.

부모 Duck을 상속한 RedHatDuck, MallardDuck이 있는 상황. 이 오리들은 fly, swim, quack을 할 수 있는데, display를 하면 화면에 서로 다른 모습으로 그려진다고 하자. 그래서 fly, swim, quack은 부모에 그냥 뒀지만 display는 추상 메소드로 둬서 자식 단에서 구현하는 모습을 확인할 수 있다.

 

이 상황에서, RubberDuck을 만드는데 얘도 Duck을 상속받아 만들려고 한다고 하자. 문제는 이 RubberDuck은 fly를 할 수 없고, quack(꽥!!)역시 살아있는 오리들의 꽥이 아니라 모형오리를 누르면 나오는 squack이라고 하자. 

 

...어떻게 이 상황을 처리할 수 있을까?

 

뭐 우선 RubberDuck이 자신의 메소드로 squack을 가지게 하고..

fly가 문젠데, 이 녀석을 오버라이딩하는 방법이 있을 것이다!

뭐 그러면 다음과 같은 꼴이 된다

.그러나 애당초 상속을 하는 이유가 뭐였는가? 코드의 재사용 떄문이다. 러버덕부터 시작해 이런 식으로 오버라이딩을 통해 해결한다면 결국 어느 순간에 이르러서는 상속의 의미가 없어지게 된다..(암튼 뭐 이 방법은 객체지향적인 관점에서 썩 좋은 방법은 아니라는 얘기)

 

 

그렇다면 어떻게 할 수 있을까..

 

메소드, 행위 자체를 인터페이스 클래스로 바꾼다면 어떨까! 기존 부모에 있는 fly, quack을 따로 빼서 flyable, quackable이란 클래스로 만들고 나중에 fly가 가능한 애들만 flyable을 상속받고 quack이 가능한 애들만 quackable을 상속받게 하는 것이다.

후..draw.io에서 그리다가 화딱지나서 파워포인트로 그렸다..

 

 

RubberDuck입장에선 쓰지도 않는 fly와 quack을 상속받지 않으니 으흠 깔끔하게 만들었군!이란 생각이 든다.

 

그러나...이 방법은 Multiple Inheritance, 즉 다중 상속이 많아지는 방식이므로 구조가 매우 복잡해질 수 있는 단점이 있기 때문에 역시나 완전 좋은 방법은 아니다.

 

방금 방법에서 메소드와 행위 자체를 인터페이스 클래스로 분리시켰음에 주목하자. 부모클래스 Duck이 이 메소드클래스와  association을 갖게 하면 어떨까? 다음과 같이 작성할 수 있다!

객체가 할 있는 행위들 중 바뀌는 행위들을 따로 클래스로 만든 모습을 볼 수 있다. 아까 Pet 인터페이스를 자식 Dog, Cat이 구현하고 Pet변수에 Dog객체, Cat객체를 대입하여 같은 메소드지만 서로 다른 일이 일어나게 했었는데(다형성!), 똑같이 여기서도 Duck에 멤버변수로 FlyBehavior변수, QuackBehavior변수를 둔 다음 이 변수들에 서로 다른 구현객체(FlyWithWings..)를 대입하여 서로 다른 동작이 일어나게 할 수 있다. 이를 테면,

 

public class MallardDuck extends Duck(){
    MallardDuck(){
        flyableBehavior = new FlyWithWings();
        quackableBehavior = new AnimalQuack();
    }
    // .. 생략
}

 

이런 식이다. 만약 나중에 새로운 나는 방식(예를 들어 로켓으로 난다든지..)을 도입해야 한다면 단순히 FlyBehavior를 구현하는 클래스 FlyWithRocket을 만들면 끝이다. 기존 코드를 건드리지 않으면서 확장이 가능한 셈이다.

 

이렇듯, 전략패턴(strategy pattern)은 메소드, 알고리즘, 행위 등을 정의하고 캡슐화하여 독립적으로 변경할 수 있게끔 하는 패턴이다. 

+ Recent posts