옛글/프로그래밍이야기

프로그래밍 Design Pattern 이해하기 - 12 컴파운드 패턴

ShakeJ 2014. 10. 20. 15:01
반응형

패턴들의 패턴들 (컴파운드 패턴)


 같이 일하라!


 패턴과 다른 패턴을 상호작용하며 사용하는 것을 바로 이 '컴파운드 패턴'이라고 부른다! 즉 컴파운드 패턴은, 패턴들을 사용하는 패턴이다. 


 즉 컴파운드 패턴은 특정 패턴이 아니라, 반복적으로 생길 수 있는 문제를 해결하기 위해 '두개 이상의 패턴을 결합'하여 사용하는 것을 의미한다. 


 우리는 이번 챕터를 통해 'SimUDuck'이라는 Duck simulator라는 예제를 만들어 볼 것이다. 패턴들을 결합해서 오리들을 만들 것이다. 많은 문제들에 이 패턴을 적용해서 문제를 풀 수 있다. 이 컴파운드 패턴이 모든 문제에 대한 정답이 될 순 없지만, 많은 문제들을 푸는데 도움이 되는 것은 확실하다. 


 자 이제 다시 오리 예제를 만들어보자. 


 첫번째로, 우리는 Quackable interface를 만들어보자. 


public interface Quackable {

public void quack();

}


 두번째는, 몇몇 오리클래스들을 만들고 이 인터페이스를 상속 받는다. 


public class MallardDuck implements Quackable{

public void quack() {

System.out.println("Quack");

}

}



public class RedheadDuck implements Quackable{

public void quack() {

System.out.println("Quack");

}

}


 물론 첫번째 시간에 했던 예제와 같이 다르게 우는 오리는 아래처럼 만들 수 있다. 


public class DuckCall implements Quackable {

public void quack() {

System.out.println("Kwak");

}

}


public class RubberDuck implements Quackable {

public void quack() {

System.out.println("Squeak");

}

}


자 이제 만들은 오리들을 사용해보자.


public class DuckSimulator {

public static void main(String[] args){

DuckSimulator simulator = new DuckSimulator();

simulator.simulate();

}


void simulate() {

Quackable mallardDuck = new MallardDuck();

Quackable redheadDuck = new RedheadDuck();

Quackable duckCall = new DuckCall());

Quackable rubberDuck = new RubberDuck();


System.out.println("Duck simulator");


simulate(mallardDuck);

simulate(redheadDuck);

simulate(duckCall);

simulate(rubberDuck);

}


void simulate(Quackable duck) {

duck.quack();

}

}







그런데 오리만 있는 경우, 우리는 거위를 처리할 수 없다!


public class Goose {

public void honk() {

System.out.println("Honk");

}

}


이럴 때 어떤 패턴을 써야 할까? 바로 아래 두줄을 블럭잡으면 답이 나온다.

우리는 이런 경우 사용하는 '어댑터'를 알고 있다. 바로 오리 아답터(goose Adapter)이다. 

+ 어댑터패턴


public class GooseAdapter implements Quackable {

Goose goose;


public GooseAdapter(Goose goose){

this.goose = goose;

}


public void quack(){

goose.honk();

}

}


public class DuckSimulator {

public static void main(String[] args){

DuckSimulator simulator = new DuckSimulator();

simulator.simulate();

}


void simulate() {

Quackable mallardDuck = new MallardDuck();

Quackable redheadDuck = new RedheadDuck();

Quackable duckCall = new DuckCall());

Quackable rubberDuck = new RubberDuck();

Quackable gooseDuck = new GooseAdapter(new Goose());


System.out.println("Duck simulator");


simulate(mallardDuck);

simulate(redheadDuck);

simulate(duckCall);

simulate(rubberDuck);

simulate(gooseDuck);

}


void simulate(Quackable duck) {

duck.quack();

}

}


정상적으로 동작이 가능하다!





자 이번엔 오리들이 울 때 몇번을 울지 숫자를 통해 제어하고 싶다. 

이럴 때 어떤 패턴을 써야 할까? 바로 아래 두줄을 블럭잡으면 답이 나온다.

바로 데코레이터 패턴이다. 데코레이터를 이용해서 오리들의 새로운 행동들을 데코레이터 객체로 싸버리는 것이 가능하다. 


public class QuackCounter implements Quackable {

Quackable duck;

static int numberOfQuacks;


public QuackCounter(Quackable duck){

this.duck = duck;

}


public void quack(){

duck.quack();

numberOfQuacks++;

}


public static int getQuacks(){

return numberOfQuacks;

}

}



자 이 데코레이터 패턴을 적용해서 테스트 드라이브 코드를 짜보자











그런데 이렇게 만들다보니, 꽥꽥 우는 숫자를 가져오기 위한 QuackCounter로 장식이 되지 않은 오리가 생길까봐 걱정이 되었다. 

그래서 이번엔 바로 팩토리 패턴을 써서 오리들을 만들기로 했다.

오리를 만드는 과정을 감싸보자. 


public abstract class AbstractDuckFactory {

public abstract Quackable createMallardDuck();

public abstract Quackable createRedHeadDuck();

public abstract Quackable createDuckCall();

public abstract Quackable createRubberDuck();

}


public class DuckFactory extends AbstractDuckFactory {

public Quackable createMallardDuck(){

return new MallardDuck();

}


public Quackable createRedHeadDuck(){

return new RedHeadDuck();

}


public Quackable createDuckCall(){

return new DuckCall();

}


public Quackable createRubberDuck(){

return new RubberDuck();

}

}


이렇게 만든 경우 Decorated object인 QuackCountDuck을 만드는 Factory과정을 아래와 같이 바꿀 수 있다. 


public class CountingDuckFactory extends AbstractDuckFactory {

public Quackable createMallardDuck(){

return new Quackable(new MallardDuck());

}


public Quackable createRedHeadDuck(){

return new Quackable(new RedHeadDuck());

}


public Quackable createDuckCall(){

return new Quackable(new DuckCall());

}


public Quackable createRubberDuck(){

return new Quackable(new RubberDuck());

}

}




그러다보니 오리가 너무 많아져서 오리를 떼로 관리를 해야 할 필요가 느껴졌다. 그래서 이번엔 컴포지트 패턴을 적용한다. 


public class Flock implements Quackable {

ArrayList quackers = new ArrayList();


public void add(Quackable quacker){

quacker.add(quacker);

}


public void quack() {

Iterator iterator = quacker.iterator();

while(iterator.hasNext()){

Quackable quacker = (Quackable) iterator.next();

quacker.quack();

}

}

}





이번에는 꽥 소리를 냈을 때 울었다는 것을 알림받고 싶어한다! 그래서 옵저버 패턴을 적용해본다. 


public interface QuackObservable {

public void registerObserver(Observer observer);

public void notifyObservers();

}


public interface Quackable extends QuackObservable {

public void quack();

}


public class Observable implements QuackObservable {

ArrayList observers = new ArrayList();

QuackObservable duck;


public Observable(QuackObservable duck){

this.duck = duck;

}


poublic void registerObserver(Observer observer){

observers.add(observer);

}


public void notifyObservers(){
    Iterator iterator = observers.iterator();

    while(iterator.hasNext()){

Observer observer = (Observer) iterator.next();

observer.update(duck);

    }

}

}


기존 Duck 에 Observable class 적용 


public class MallardDuck implements Quackable {

Observable observable;


public MallardDuck() {

observable = new Observable(this);

}


public void quack(){

System.out.println("Quack");

notifyObservers();

}


public void registerObserver(Observer observer){

observable.registerObserver(observer);

}


public void notfiyObservers() {

observable.notifyObservers();

}

}


public interface Observer {

public void update(QuackObservable duck);

}


public class Quackologist implements Observer {

public void update(QuackObservable duck){

System.out.println("Quacklogist : " + duck + " just quacked");

}

}










즉 처음엔 수많은 Quackable 객체들이 존재한다. 

- 갑자기 거위가 나타난다! (어댑터 패턴)

- 꽥꽥 거리는 횟수를 알고싶어졌음 (데코레이터 패턴)

- QuackCounter로 장식되지 않은 Quackable 객체가 있을 것을 걱정 (추상팩토리 패턴)

- Quackable객체들이 많아져서 관리가 되지 않아 '오리 떼'를 만듬 (컴포지트 패턴)

- 꽥 울었을 때 어떤 오리가 울었는지 알고 싶어졌다 (옵저버 패턴)


모델 - 뷰 - 컨트롤러와의 만남












반응형