ios dev kangwook.

15. Compound Pattern 본문

Design Pattern

15. Compound Pattern

kangwook 2022. 9. 7. 23:41

Compound Pattern은 두 개 이상의 패턴을 결합해 일반적으로 자주 등장하는 문제들에 대한 해법을 제공 

Compound Pattern

  • 컴파운드 패턴은 딱히 정해진 의미나 장단점이 뚜렷하지 않음
  • 상황에 맞게 서로 다른 패턴을 섞어쓰는 디자인 패턴이기 때문
  • 주의할 점은 패턴 몇 개를 결합해서 쓴다고 무조건 컴파운드 패턴이 되는 것이 아님
    • 컴파운드 패턴이라 불리기 위해서는 여러 문제를 해결하기 위한 용도로 쓰일 수 있는 일반적인 해결책이어야 함Context : State를 이용하는 역할을 수행

Compound Pattern 예제

  • 1장에서 작성했던 Duck 예제의 리팩토링
  • Quackable 인터페이스 정의No Quarter : 동전 없음
public interface Quackable{
    public void quack();
}
  • 완전한 오리는 아니지만 오리의 행동을 할 수 있는 오리 호출기(DuckCall), 모형 오리(RubberDuck)
 
public class DuckCall implements Quackable {
    public void quack() {
        System.out.println("kwak");
    }
}
 
public class RubberDuck implements Quackable {
    public void quack() {
        System.out.println("Squack");
    }
}
  • 거위 클래스를 추가하고 Duck이 들어가는 곳에 거위도 들어갈 수 있게 하기 : 거위용 어댑터
    • 거위는 오리와 다르게 울기 때문에 quack() 메소드 대신 honk() 메소드를 가지고 있음
    • 거위용 어댑터를 생성해서 거위를 오리처럼 사용
public class GooseAdapter implements Quackable {
    Goose goose;
  
    public GooseAdapter(Goose goose) {
        this.goose = goose;
    }
   
    //quack()메소드가 호출되면 goose의 honk() 메소드를 호출한다.
    public void quack() {
        goose.honk();
    }
}
  • 오리 클래스는 그대로 두면서 오리떼가 꽥꽥한 횟수를 세어보기 : 데코레이터 패턴
    • 꽥 소리를 카운트해주는 기능을 추가하기 위한 데코레이터를 만들어 Duck 객체를 감싸기
public class QuackCounter implements Quackable {
    Quackable duck;
    static int numberOfQuacks;
    //모든 객체의 회수를 세야하기 때문에 정적 변수 사용
   
    public QuackCounter (Quackable duck) {
        this.duck = duck;
    }
   
    public void quack() {
        //quack 메소드를 호출하면 데코레이터 안에 있는 duck객체에 위임하고
        //numberOfQuacks 를 증가한다.
        duck.quack();
        numberOfQuacks++;
    }
  
    public static int getQuacks() {
        return numberOfQuacks;
    }
}
  • 오리를 생산하기 위한 패턴 : 추상 팩토리 패턴
    • 오리를 생성할 때마다 데코레이터로 감싸지 않으면 정확히 카운트를 할 수 없음
    • 오리를 생성하는 작업을 한 곳에서 몰아서 하도록 함
public abstract class AbstractDuckFactory {
    public abstract Quackable createMallardDuck();
    public abstract Quackable createRedheadDuck();
    public abstract Quackable createDuckCall();
    public abstract Quackable createRubberDuck();
}
 
//서브클래스 각 메소드에서 다른 종류의 오리를 생성
public class CountingDuckFactory extends AbstractDuckFactory {
    public Quackable createMallardDuck() {
        return new QuackCounter(new MallardDuck());
    }
   
    public Quackable createRedheadDuck() {
        return new QuackCounter(new RedheadDuck());
    }
   
    public Quackable createDuckCall() {
        return new QuackCounter(new DuckCall());
    }
    
    public Quackable createRubberDuck() {
        return new QuackCounter(new RubberDuck());
    }
}
  • 오리 무리를 만들어 오리들을 하나로 관리 : 컴포지트 패턴
    • Composite : Flock
    • Leaf : Quackable
    • Composite에만 자식을 관리하기 위한 기능을 넣음
public class Flock implements Quackable {
    //ArrayList로 오리떼에 속하는 오리들을 보관함
    ArrayList quackers = new ArrayList();
  
    public void add(Quackable quacker) {
        quackers.add(quacker);
    }
  
    //Flock에 들어있는 모든 오리들의 quack()을 호출해주어야함
    public void quack() {
        Iterator iterator = quackers.iterator();
        while (iterator.hasNext()) {
            Quackable quacker = (Quackable)iterator.next();
            quacker.quack();
        }
    }
  
    public String toString() {
        return "Flock of Quackers";
    }
}
  • 실시간으로 꽥꽥거리는 오리들을 각각 추적할 수 있는 기능 추가 : 옵저버 패턴
    • Observable 인터페이스 추가
public interface QuackObservable {
    //Observer 인터페이스를 구현하는 객체라면 어떤 객체는 꽥꽥거리는걸 감시할 수 있음
    public void registerObserver(Observer observer);
    public void notifyObservers();
}
  • Quackable에서 QuackObservable 인터페이스를 상속
  • Quackable을 구현하는 모든 Concrete class에서 QuackObservable도 구현하도록 해야함
public class Observable implements QuackObservable {
    ArrayList observers = new ArrayList();
    QuackObservable duck;
  
    public Observable(QuackObservable duck) {
        this.duck = duck;
    }
   
    public 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);
        }
    }
  
    public Iterator getObservers() {
        return observers.iterator();
    }
}
  • 각 오리 클래스에서 Observable 객체를 가지고 있고 QuackObservable과 관련된 모든 기능을 해당 객체에 위임
public class RubbserDuck implements Quackable {
    Observable observable;
  
    public MallardDuck() {
        observable = new Observable(this);
    }
  
    public void quack() {
        System.out.println("Squack");
        notifyObservers();
    }
  
    //QuackObservable에서 정의한 메소드들
    //모두 observable 보조 객체에 작업을 위임한다.
    public void registerObserver(Observer observer) {
       observable.registerObserver(observer);
    }
  
    public void notifyObservers() {
        observable.notifyObservers();
    }
  
    public String toString() {
        return "Rubber Duck";
    }
}
  • 마지막으로 옵저버쪽 코드를 작성
public interface Observer {
    //꽥소리를 낸 오리를 인자로 받는 메소드
    public void update(QuackObservable duck);
}
 
//꽥학자 클래스
public class Quackologist implements Observer {
    public void update(QuackObservable duck) {
        System.out.println("Quackologist: " + duck + " just quacked.");
    }
}
패턴은 반드시 상황에 맞게 사용해야 한다!

본문은 Head First Design Pattern (2004) 를 참고하여 공부하고 작성하였음


 

'Design Pattern' 카테고리의 다른 글

14. Proxy Pattern  (0) 2022.09.06
13. State Pattern  (0) 2022.09.05
12. Composite Pattern  (0) 2022.09.04
11. Iterator Pattern  (0) 2022.08.31
10. Template Method Pattern  (0) 2022.08.29
Comments