티스토리 뷰

 

복합패턴 - 여러 패턴을 함께 사용하는 것

 

복합 패턴 예제

- 오리 시뮬레이션 게임

public interface Quackable {
    public void quack();
}

public class MallardDuck implements Quackable {
    public void quack() {
        System.out.println("꽥꽥");
    }
}

public class DuckCall implements Quackable {
    public void quack() {
        System.out.println("꽉꽉");
    }
}

public class DuckSimulator {
    public static void main(String[] args) {
        DuckSimulator simulator = new DuckSimulator();
        simulator.simulate();
    }

    void simulate() {
        Quackable mallardDuck = new MallardDuck();
        Quackable duckCall = new DuckCall();

        System.out.println("\n오리 시뮬레이션 게임");

        simulate(mallardDuck);
        simulate(duckCall);
    }

    void simulate(Quackable duck) {
        duck.quack();
    }
}

 

거위를 추가하려고함

-> 어댑터 패턴(호환성 인터페이스)을 사용

 

public class Goose {
    public void honk() {
        System.out.println("끽끽");
    }
}

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 duckCall = new DuckCall();
        Quackable goose = new GooseAdapter(new Goose()); // Goose를 어댑터로 감싸줌
        
        System.out.println("\n오리 시뮬레이션 게임");

        simulate(mallardDuck);
        simulate(duckCall);
        simulate(goose);  // 거위지만 오리 메소드 실행 가능
    }

    void simulate(Quackable duck) {
        duck.quack();
    }

 

 

오리가 소리를 낸 횟수를 count 하려고 함

-> 오리가 소리를 낸 횟수를 count 하기 위해 데코레이터 패턴(객체에 기능추가) 사용

 

// 데코레이터 클래스 QuackCounter

public class QuackCounter implements Quackable {
    Quackable duck;
    static int numberOfQuacks; // 모든 객체에서 count해야해서 정적 static 사용

    public QuackCounter(Quackable duck) {
        this.duck = duck;
    }

    public void quack() {
        duck.quack();
        numberOfQuacks++;
    }

    public static int getQuacks() {
        return numberOfQuacks;
    }
}

 

 

Quackable을 채택하는 QuackCounter

Quackable 객체를 생성할때 QuackCounter 데코레이터로 감싸줌.

 

데코레이터의 quack() 메소드 실행시 -> 소리냄 + count 동작을 하도록 함

 

public class DuckSimulator {
    public static void main(String[] args) {
        DuckSimulator simulator = new DuckSimulator();
        simulator.simulate();
    }

    void simulate() {
        Quackable mallardDuck = new QuackCounter(new MallardDuck());
        Quackable duckCall = new QuackCounter(new DuckCall());
         // Quackable을 데코레이터로 감싸 줌
        
        Quackable goose = new GooseAdapter(new Goose());
       

        System.out.println("\n오리 시뮬레이션 게임");

        simulate(mallardDuck);
        simulate(duckCall);
        simulate(goose);

        System.out.println("오리가 소리 낸 횟수 : " + QuackCounter.getQuacks() + " 번");
    }

    void simulate(Quackable duck) {
        duck.quack();
    }
}

 

 

새로운 행동을 추가 하기위해선 데코레이터로 감싸야함

-> 모든 오리를 데코레이터로 감싸려고 함

-> 팩토리 패턴 사용

-> 여러 오리의 종류를 생산해야 하므로

-> 추상 팩토리 패턴을 사용하여 캡슐화

 

public abstract class AbstractDuckFactory {
    public abstract Quackable createMallardDuck();
    public abstract Quackable createDuckCall();
}

public class DuckFactory extends AbstractDuckFactory {
    // 각 메소드는 Quackable 객체를 만들고
    // 시뮬레이터는 뭐가 만들어지는지는 모르며, 
    // Quackable형태가 리턴되는것만 알게 됨
    
    public Quackable createMallarDuck() {
        return new MallardDuck();
    }
    public Quackable createDuckCall() {
        return new DuckCall();
    }
}

public class CountingDuckFactory extends AbstractDuckFactory {
    public Quackable createMallardDuck() {
        return new QuackCounter(new MallardDuck());
    }
    
    public Quackable createDuckCall() {
        return new QuackCounter(new DuckCall());
    }
}

public class DuckSimulator {
    public static void main(String[] args) {
        DuckSimulator simulator = new DuckSimulator();
        AbstractDuckFactory duckFactory = new CountingDuckFactory();

        simulator.simulate(duckFactory); // simulate에 전달할 팩토리 생성 후 전달
    }

    void simulate(AbstractDuckFactory duckFactory) {
        Quackable mallardDuck = duckFactory.createMallardDuck();
        Quackable duckCall = duckFactory.createDuckCall();
        // 팩토리 메소드에서 객체 생성 해줌
        
        Quackable goose = new GooseAdapter(new Goose()); 
        

        System.out.println("\n오리 시뮬레이션 게임");

        simulate(mallardDuck);
        simulate(duckCall);
        simulate(goose)

        System.out.println("오리가 소리 낸 횟수 : " + QuackCounter.getQuacks() + " 번");
    }

    void simulate(Quackable duck) {
        duck.quack();
    }
}

 

 

CountingDuckFactory를 사용하면

모든 오리를 데코레이터로 확실하게 감쌀 수 있음.

 

 

 

오리를 1마리씩 관리하는것보다 컴포지트 패턴을 사용하여

무리로 관리하면 좋음

 

public class Flock implements Quackable {
    List<Quackable> quackers = new ArrayList<Quackable>();

    public void add(Quackable quacker) {
        quackers.add(quacker);
    }

    public void quack() {
        Iterator<Quackable> it = quackers.iterator(); // 반복자 패턴
        while (it.hasNext()) {
            Quackable quacker = it.next(); // 순환문
            quacker.quack();
        }
    }
}

 

 

public class DuckSimulator {
   // main 메소드
   
    void simulate(AbstractDuckFactory duckFactory) {
        Quackable redheadDuck = duckFactory.createRedheadDuck();
        Quackable duckCall = duckFactory.createDuckCall();
        Quackable rubberDuck = duckFactory.createRubberDuck();
        Quackable gooseDuck = duckFactory.createGoose();
        
        System.out.println("\n오리 시뮬레이션 게임 : 무리 (+컴포지트)");

        Flock flockOfDucks = new Flock(); // 오리 무리

        flockOfDucks.add(redheadDuck);
        flockOfDucks.add(duckCall);
        flockOfDucks.add(rubberDuck);
        flockOfDucks.add(gooseDuck);

        Flock flockOfMallards = new Flock(); // 물오리 무리 (물오리만 포함)
        
        // 물오리 무리 생성
        Quackable mallardOne = duckFactory.createMallardDuck();
        Quackable mallardTwo = duckFactory.createMallardDuck();
        Quackable mallardThree = duckFactory.createMallardDuck();
        Quackable mallardFour = duckFactory.createMallardDuck();

        flockOfMallards.add(mallardOne);
        flockOfMallards.add(mallardTwo);
        flockOfMallards.add(mallardThree);
        flockOfMallards.add(mallardFour);

        flockOfDucks.add(flockOfMallards);  // 물오리 무리를 오리 무리에 추가

        System.out.println("오리 시뮬레이션 게임 : 전체 무리");
        simulate(flockOfDucks);

        System.out.println("오리 시뮬레이션 게임 : 물오리 무리");
        simulate(flockOfMallards);

        System.out.println("오리가 소리 낸 횟수 : " + QuackCounter.getQuacks() + " 번");
    }

    void simulate(Quackable duck) {
        duck.quack();
    }
}

 

 

오리 각각 1마리의 행동을 관찰하고 싶음. (어떤 오리가 소리를 내었는지)

-> 옵저버 패턴을 사용

 

 

옵저버블 인터페이스

// QuackObservable 인터페이스
public interface QuackObservable { // Quackable를 다른 객체에서 관측하고자 함
    public void registerObserver(Observer observer); // 옵저버 등록 메소드
    public void notifyObservers(); // 옵저버 알림 메소드
}

// 모든 Quackable에서 이 인터페이스를 구현하도록 해야함
public interface Quackable extends QuackObservable {
    public void quack();
}

 

 

옵저버 인터페이스

public interface Observer { 
    // 소리를 내는 QuackObservable를 인자로 전달받는 update 메소드
    public void update(QuackObservable duck);
}

public class Quackologist implements Observer {
    public void update(QuackObservable duck) {
        System.out.println("꽥꽥학자 : " + duck + "가 소리냈다.");
    }
}

 

 

Observable에서 Quackable이 관찰 대상이 될 때 필요한 기능을 정의함.

Observable 객체를 다른 클래스에 넣은 다음 필요한 작업을 Observable에 위임하려고 함.

 

public class Observable implements QuackObservable {
    List<Observer> observers = new ArrayList<Observer>();
    // 생성자의 인자로 Observer 객체를 써서 관측할 객체를 전달함
    
    QuackObservable duck;

    public Observable(QuackObservable duck) {
        this.duck = duck;
    }

    public void registerObserver(Observer observer) { // 옵저버 등록
        observers.add(observer);
    }

    public void notifyObservers() {
        Iterator<Observer> it = observers.iterator();
        while (it.hasNext()) {
            Observer observer = it.next(); // 연락 돌리는 코드
            observer.update(duck);
        }
    }
}

 

 

Observer 보조 객체와  Quackable 클래스 결합해주기

 

public class MallardDuck implements Quackable {
    Observable observable; // Quackable에 Observable 변수 생성

    public MallardDuck() {
        this.observable = new Observable(this);
        // 생성자에서 Observable 객체를 만들고 
        // MallardDuck 객체의 레퍼런스를 인자로 전달
    }

    public void quack() {
        System.out.println("꽥꽥");
        notifyObservers();  // quack() 호출되면 옵저버들에게 알려줌
    }
    
    // QuackObservable 에서 정의한 메소드, 보조 객체에 떠넘김
    public void registerObserver(Observer observer) {
        this.observable.registerObserver(observer);
    }
    
    // QuackObservable 에서 정의한 메소드, 보조 객체에 떠넘김
    public void notifyObservers() {
        this.observable.notifyObservers();
    }
}

 

 

 

void simulate(AbstractDuckFactory duckFactory) {
    // 오리 팩토리와 오리 생성
    // 오리 무리 생성
    
    Quackologist quackologist = new Quackologist();
    flockOfDucks.registerObserver(quackologist);
    
    simulate(flockOfDucks);
    
    System.out.println("오리가 소리 낸 횟수 : " + QuackCounter.getDuckCnt() + " 번");
 }

 

댓글