1. 디자인 패턴 소개( 스트래티지 패턴 )
일단 패턴 소개에 앞서서 가장 기본적으로 선행 되어야 할 내용이 있다면 상속 및 인터페이스 및 다형성의 정확한 이해다.
이를 위해서 아주 간단한 소스와 내용을 적고 그리고 본문의 스트래티지 패턴에 대해서 소개를 하는 형태로 진행을 해 나가겠다
1. 왜 다형성을 이해해야 하는가?
일단 진행에 앞서 요즘 스프링 참 많이 듣지 않는가? 여기도 스프링 저기도 스프링(아 따뜻한 봄이 오는구나..).. 우리 스터디에서도 불과 몇달전에 스프링을 약간 아주 약간맛을 아주 조금봤다. 그렇다 아주조금... 하고 싶은 이야기는 이 스프링을 공부 할때 dependency injection 이라는 개념이 있었는데 이게 지금 설명 하려는다형성을 기반으로 이해를 해야 하기 때문이다. 왜냐구 물어보면.. dependency injection 이라는게 살펴보고 나면 결국 스트래티지 패턴 하고 다를게 전혀 없기 때문이다.
즉 스프링의 dependency injection <= 스트래티지 패턴 <= 다형성 이런 과정이 성립 하기 때문에 무엇보다 앞서서 다형성을 설명 하려 한다. 말이 길었다 소스로 보자.
------------------------------------------------------------------------------------------------------------------------
public class Parent {
private String name = "부모";
public void printName(){
System.out.println("name" + name);
}
}
public class A extends Parent() {
public void printName(){
System.out.println("난 자식이다");
}
}
Parent a6 = new A();
a6.printName();
------------------------------------------------------------------------------------------------------------------------
위의 코드는 설명이 필요 없을거 같다. 메인을 구현해서 테스트해 보면 다형성이 어떤건지 바로 확인 할수 있을것이다.
잘이해가 안가거나 좀더 자세한 설명을 원하신다면 다음 링크를 따라가서 읽어 보기 바란다.
위에서 보여진 예제와 같이 다형성은 별게 아니다. 정말 그럴까? 아무것도 모를땐 ..그게 머야 당연한거아니야 별거 아니네 라고 답할수 있겠지만 이게 어떻게 쓰이는지 보게 된다면 별게 아니다 라고 하는게 아니라 "오 주여" 를 외치게 될거이다. 이제 외치러 가보자.
2. 문제에 대한 인지..
'문제에 대한 인지' 그렇다 이 말은 정말 중요하다. 모든 것에 앞서서 현재 어떤문제가 있는지 깨닫는 과정은 정말 중요한 시간이다. 문제가 무엇인지 알아야 솔루션을 제공할것이 아닌가?
지금부터는 소스를 보면서 어떻게 '문제를 인지'하게 되는지 같이 살펴보자.
상황은 다음과 같다. 아래 그림과 같이 오리클래스로 부터 상속을 받은 2개의 오리가 있다 MallaaedDuck 와 RedHeadDuck 이 그것 이다.
이녀석들은 오리로 부터 상속을 받았기에 당연히 울수있고(quack()) 물에뜨고(swim()) 각각의 모습이 틀리기에(한놈음 머리가 빨간색이지 않는가?) 오버라이딩한 display() 메소드를 가진다.
사용자는 문득 오리도 날수 있다라는 사실을 인지 하였기에 Duck클래스에 fly() 메소드를 추가 하기로 하였다. 그리고 RubberDuck(고무오리) 클래스를 추가 하였다.
위 상황에서 상속이 가진 힘에 의해 RubberDuck 역시 울수있고(quack()) 물에뜨고(swim()) 고유의 외모(display()) 그리고 날수(fly())하게 되었다.
고무오리는 날지 못한다. 더군다나 울수도 없고 말이다.
우리는 이문제를 해결하기 위해 처음에 오리마다 각각의 고유의 모습을 그려내었던(display()) 방법인 오버라이딩을 해결책으로 떠올릴수 있고.
행동 즉 메서드를 상속을 통해서 제공하므 인해 발생 하는 문제에 대해서 깨달을수 있다.(문제의 인지)
첫번째 문제는 서브 클래스에 코드가 중복이 된다는점.
두번째 문제는 모든 오리의 행동을 알기 힘들다는점.(어떤오리가 추가 될지 어떻게 알수 있단 말인가???)
세번재 문제는 코드를 변경했을때 (또는 새로운 행동을 추가 했을때) 다른 오리들에게 원치 않는 영향을 끼칠수 있다는점.
네번째 문제는 실행시점 그러니까 오리를 만들어내는 시점에 특징을 바꾸기 힘들다는 것이다.(이건 믿에서 알아 보도록 하자)
2.1 첫번재 솔루션
우리는 경험을 통해서 배운다. 글렇지 않은가? 위에서 고무오리를 하나 추가해 봄으로써 앞으로 어떤일이 펼처질수 있을지 상상 할수 있었다.(어떤 오리가 튀어 나올지 모른다 )
예전에 자바 기초를 배울때 이런 내용을 들은거 같다. 메소드의 선언만 가지고 있고 구현은 안된 인터페이스란게 있다라고..
그렇다 인터페이스를 이용해서 지금 문제를 일으키고 있는 행동들을 분리하자 라는 생각에 Flayble 과 Quackable 인터페이스를 만들고 모든 오리는 이걸 구현하게 하자!
일단 보기만 해도 문제가 있어보인다. 울지 못하고 날지 못하는 1개의 오리 때문에 나머지 오리들이 전부 2개의 인터페이스를 가져다가 구현한다....
2.2 두번재 솔루션
이제 문제는 명확해 졌다. 클레스에서 변화는 부분과 변화지 않는 부분을 분리해 내는게 첫번째 이고. 전체가 아닌 일부만 날수 있거나 울수 있게 혹은 울수 없게 만들어야 한다.
이제..드디어 다형성을 쓸 시간이 왔다. 그분이 오실 시간 이란 말이다. 다음을 보자.
이그림을 볼때 바로 소스가 어떻게 그려질지 머리에 그려지는 분도 있을것이다. 많은 노력과 좋은 환경 그리고 시간을 필요로 했을거라 생각이 된다. 이 문서를 작성하는 이유는
그러지 못한 사람들을 위해서 이므로 차근 차근 풀어 보도록 하자.
일단 추상 클래스 Duck 부터 살펴 보면 2개의 인터페이스 형식의 레퍼런스변수(참조변수)를 선언 하고 있다. 그리고 performQuack 과 performFly 2개의 메소드를 통해서 어떤 행동을
(꽥꽥 울어야 하는지 아니면 ?꽉 울어야 하는지 혹은 날개로 날아야 하는지 로켓으로 날아야 하는지 아니면 날수 없던지) 취해야 할지 행동을 제어하는 인터페이스에 위임 한다.
이 위임 이라는 용어가 조금 눈에 거슬릴거 같은데, 별거 아니다 그냥 넘겨준다는 뜻이다.
지금까지 기술한 내용을 코드로 써보면 다음과 같다.
------------------------------------------------------------------------------------------------------------------------
public abstract class Duck{
Flyable flyBehavior;
Quackable quackBehavior;
public abstract void display();
public void performQuack() {
quackBehavior.quack()
}
public void performFly(){
flyBehavior.fly();
}
public void setFlyBehavior(Flyable fb){
flyBehavior = fb;
}
public void setQuackBehavior(Quackable qb){
quackBehavior = qb
}
}
------------------------------------------------------------------------------------------------------------------------
코드 자체는 전혀 어려운게 없다. 핵심은 performQuack(), performFly() 이 두개의 메소드다. 위에서 기술한 다형성의 내용을 기억 하고 있다면 이게 어떻게 쓰일지 그림이 그려질 것이다.
setFlyBehavior()와 setQuackBehavior()의 인자인 Quackable 과 Flyable 는 아규먼트 다형성 혹은 프로모션이 라고 불리는 다형성의 형질을 이용해 값을 넘기는 아규먼트다 메인메소드 에서는 아마 다음과같이 구현될걸다
------------------------------------------------------------------------------------------------------------------------
.
.
Flyable fb = new FluWithWings();
Quackable qb = new Squack();
xxx.setFlyBehavior(fb); // 한줄로쓰면 다음과 같이 된다. xxx.setFlyBehavior(new FluWithWings());
xxx.setQuackBehavior(qb);
xxx.performQuack();
xxx.performFly();
.
.
------------------------------------------------------------------------------------------------------------------------
이렇게 만들면 부모의 메소드로 자식의 메소드를 호출하는 메서드 다형성이 발생되는 형태를 된다.
위와 같이 위임할 객체를 선택하는 seter를 만들어 두면 실행 시점에 행동에 대한 제어가 쉬워진다. Duck 클래스를 손대지 않고 단순히 performQuack과 performFly 메소드를 호출
하는것많으로 행동되야할 객체를 선택할수 있게 된다.
자 여기 까지 왔는데.. 이 스트래티지 패턴이 "어떤점이 좋은지 잘 모르겠다" 라고 하신 다면 처음에 "문제의 인지"를 할수 있었던 방법을 써보자.
이번에 태안 사고로 인해 유전자가 이상해진 오리가 나타난 거다. 이 오리는 보통오리와 달라서 로켓 추진으로 날수 있게 되었다 라고 해보자.
이 이오리가 가진 특성을 클래스에 추가하려면 어떻게 하면 될까?
간단하다. Flyable 인터페이스를 구현한 RoketWithWings라는 클래스를 하나 추가만 하면된다. 다음과 같이 말이다
멋지지 않는가?? 기능이 하나 추가 되었다고 Duck를 변경 하고 그를 상속받은 각각의 클래스를 수정 하는 이런 소모적인 일을 할필요가 전여 없다. 그렇다. 이건 영어식 표현으로 하면 Cool 한 거다. 이 패턴 꼭 기억 하고 가자
'Design Patterns' 카테고리의 다른 글
Command Pattern 알아보기- (0) | 2009.12.30 |
---|---|
Singleton Pattern - 바로 알기(HeadFirst Design Patterns #5) (0) | 2009.12.23 |
스트래티지 패턴(Strategy Pattern) (0) | 2009.11.12 |
Observer Pattern (0) | 2009.11.11 |
Singleton Pattern - (0) | 2009.11.11 |