관리 메뉴

bright jazz music

Compound Pattern: 6. 지금까지의 내용 리뷰 본문

Design Pattern/Compound Pattern

Compound Pattern: 6. 지금까지의 내용 리뷰

bright jazz music 2022. 11. 5. 13:35

https://github.com/hojuncha997/designPatternStudy/tree/main/src/main/java/com/example/designpatternstudy/compoundPattern

 

GitHub - hojuncha997/designPatternStudy: design pattern study

design pattern study. Contribute to hojuncha997/designPatternStudy development by creating an account on GitHub.

github.com

 

1. QuackableInterfaceForCompoundPattern 생성 (오리 생성)

  • 처음엔 QuackableInterfaceForCompoundPattern 인터페이스를 만들었다.
  • 이 인터페이스는 오리 소리를 낼 수 있는여러 종류의 오리를 만들기 위해 작성되었으며 public void Quack() 메소드를 가지고 있다.
  • MallardDuck, RedHeadDuck, DuckCall, RubberDuck 과 같은 오리 클래스들이 이 인터페이스를 구현(implements)하였다. 
//QuackableInterfaceForCompoundPattern.java

package com.example.designpatternstudy.compoundPattern;

public interface QuackableInterfaceForCompoundPattern {
    public void quack();
}

 

 

2.  Adapter Pattern (GooseAdapter 생성)

  • GooseAdapter는 Goose(거위)를 오리(QuackableInterfaceForCompoundPattern)로 만들기 위해 만든 클래스이다.
  • Goose클래스는 오리가 아니므로 QuackableInterfaceForCompoundPattern인터페이스를 구현하지 않는다. 따라서 오리만 사용 가능한 프로그램에서는 사용할 수 없다.
  • Goose가 QuackableInterfaceForCompoundPattern를 구현하게 만든다면 오리가 되므로 해당 프로그램을 사용할 수 있겠지만  기존 코드가 수정되므로 Open-Closed Principle을 위반한다. 또한 해당 클래스는 형식적으로는 오리겠지만 실질적으로는 오리도, 거위도 아닌 상태가 되어버린다.
  • 따라서 Goose를 오리로 만들 수 있는 GooseAdapter를 생성한다. 이 때 GooseAdapter는 QuackableInterfaceForCompoundPattern 인터페이스를 구현(implements)해야 하며 Goose를 멤버 변수로서 가져야 한다.
  • public void quack()을 오버라이드 하면서 그 내부의 기능을 멤버 변수 Goose에게 위임한다.
  • QuackableInterfaceForCompoundPattern gooseDuck = new GooseAdapter(new Goose());와 같이 객체를 생성한다.
//어댑터 클래스에서는 타겟 인터페이스를 구현해야 한다.
// 여기서는 QuackableInterfaceForCompoundPattern이 타겟
public class GooseAdapter implements QuackableInterfaceForCompoundPattern {

    Goose goose;

    //생성자
    public GooseAdapter(Goose goose){
        this.goose = goose;
    }

    @Override
    public void quack() {
        goose.honk();
    }

}

 

 

3. Decorator Pattern(QuackCountingDecorator 생성)

  • 오리가 소리낸 횟수를 세고 싶었기 때문에 데코레이터 패턴을 사용하였다. 데코레이터 패턴은 기존의 코드를 수정하지 않으면서 새로운 기능을 추가할 때 사용하는 패턴이다.
  • 우선 QuackCountingDecorator.java를 생성하였다. 이 클래스 역시 QuackableInterfaceForCompoundPattern 인터페이스를 구현한다. GooseAdapter가 Goose 객체를 감싸듯이, QuackCountingDecorator는 오리 객체를 감싸줘야 하기 때문이다.
  • QuackableInterfaceForCompoundPattern mallardDuck = new QuackCountingDecorator(new MallardDuck());와 같이 객체를 생성한다.
//QuackCountingDecorator.java
package com.example.designpatternstudy.compoundPattern;

//데코레이터. 어댑터와 마찬가지로 타겟 인터페이스 구현 필요
public class QuackCountingDecorator implements QuackableInterfaceForCompoundPattern {
    //감싸고자 하는 객체의 레퍼런스를 저장하는 인스턴스 변수 필요
    QuackableInterfaceForCompoundPattern duck;
    static int numberOfQuacks;

    //생성자: 생성자에서 감싸고 있는 타겟 인터페이스의 레퍼런스를 가져온다.
    public QuackCountingDecorator(QuackableInterfaceForCompoundPattern duck){
        this.duck = duck;
    }

    @Override
    public void quack() {//호출 시 내부의 객체에게 동작을 위임.
        duck.quack();
        numberOfQuacks++;
    }

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

 

4. Factory Pattern (AbstractDuckFactory, CountingDuckFactory, DuckFactory 생성)

  • 데코레이터를 사용해서 각각의 오리 객체를 감싸주는 일은 번거로울 뿐만 아니라 위험한 작업이다. 프로그램이 복잡해지면 해당 작업을 누락할 수도 있기 때문이다.
  • 따라서 추상 팩토리 패턴을 사용하여 팩토리가 객체를 만들도록 하였다.
  • 오리 객체를 만들 때는 항상 팩토리에 요청하므로 팩토리가 데코레이터에 싸여 있는 오리를 리턴하게 하였다.(애초에 소리 카운팅 기능이 있는 오리가 생산돼 나오는 것이므로 누락 염려가 없다.)
  • QuackCountingDecorator로 장식되지 않은(감싸지지 않은) 오리를 원한다면 다른 팩토리를 쓰면 된다. 실습에서는 DuckFactory 클래스를 사용하면 데코레이터로 장식되지 않은 오리가 반환된다.
//AbstractDuckFactory.java

//새로운 기능을 사용할 때는 객체를 데코레이터로 감싸야만 가능
//이 감싸는 작업을 따로 빼내어 한 군데서 하기 위해 팩토리 사용
package com.example.designpatternstudy.compoundPattern;
//추상 클래스
public abstract class AbstractDuckFactory {
    //추상 메서드
    public abstract QuackableInterfaceForCompoundPattern createMallardDuck();
    public abstract QuackableInterfaceForCompoundPattern createRedheadDuck();
    public abstract QuackableInterfaceForCompoundPattern createDuckCall();
    public abstract QuackableInterfaceForCompoundPattern createRubberDuck();

}
//CountingDuckFactory.java : QuackCountingDecorator로 감싸진 오리 객체 반환
package com.example.designpatternstudy.compoundPattern;

public class CountingDuckFactory extends AbstractDuckFactory{

    //모든 메서드에서 QuackableInterfaceForCompoundPattern객체를
    //QuackCountingDecorator데코레이터로 감싼다.
    //시뮬레이터는 데코레이터로 감싼 객체가 반환된다는 것을 알 수 없다.
    //이전과 똑같이 QuackableInterfaceForCompoundPattern를 반환받았다고 여긴다.

    @Override
    public QuackableInterfaceForCompoundPattern createMallardDuck() {
        return new QuackCountingDecorator(new MallardDuck());
    }

    @Override
    public QuackableInterfaceForCompoundPattern createRedheadDuck() {
        return new QuackCountingDecorator(new RedheadDuck());
    }

    @Override
    public QuackableInterfaceForCompoundPattern createDuckCall() {
        return new QuackCountingDecorator(new DuckCall());
    }

    @Override
    public QuackableInterfaceForCompoundPattern createRubberDuck() {
        return new QuackCountingDecorator(new RubberDuck());
    }
}
//DuckFactory.java
//데코레이터가 없는 오리 생성 팩토리
package com.example.designpatternstudy.compoundPattern;

public class DuckFactory extends AbstractDuckFactory{ //추상 팩토리 확장
    //각 메소드는 QuackableInterfaceForCompoundPattern 인터페이스 객체를 만든다.
    //시뮬레이터는 실제 어떤 제품이 만들어지는지 알 수 없다.
    //단순히 QuackableInterfaceForCompoundPattern이 리턴된다는 것만 안다.

    @Override
    public QuackableInterfaceForCompoundPattern createMallardDuck() {
        return new MallardDuck();
    }

    @Override
    public QuackableInterfaceForCompoundPattern createRedheadDuck() {
        return new RedheadDuck();
    }

    @Override
    public QuackableInterfaceForCompoundPattern createDuckCall() {
        return new DuckCall();
    }

    @Override
    public QuackableInterfaceForCompoundPattern createRubberDuck() {
        return new RubberDuck();
    }
}

 

용례

AbstractDuckFactory duckFactory = new CountingDuckFactory();

QuackableInterfaceForCompoundPattern mallardDuck = 
	duckFactory.createMallardDuck(); // 데코레이터로 감싸진 MallardDuck 반환

 

5. Composite Pattern + iterator패턴 (Flock)

모든 오리와 거위, QuackableInterfaceForCompound 객체를 관리하기 어려워졌다. 따라서 Composite 패턴을 사용하여 오리들을 모아서 무리 단위로 관리하기로 하였다. 이 패턴을 사용하면 오리를 종별로 나눠서 관리할 수도 있다.

 

//Flock.java

//오리 무리를 만드는 클래스
//composite패턴 사용: 객체들로 구성된 컬렉션을 개별 객체와 같은 방식으로 다룰 수 있게 해준다.
package com.example.designpatternstudy.compoundPattern;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

//복합객체와 잎 원소에서 같은 인터페이스를 구현해야 한다.
//여기서는 QuackableInterfaceForCompoundPattern이 잎 원소가 된다.
public class Flock implements QuackableInterfaceForCompoundPattern {

    List<QuackableInterfaceForCompoundPattern> quackers
            = new ArrayList<QuackableInterfaceForCompoundPattern>();

    //Flock에 QuackableInterfaceForCompoundPattern(오리)을 추가하는 메서드
    public void add(QuackableInterfaceForCompoundPattern quacker){
        //List에 새로운 QuackableInterfaceForCompoundPattern(오리)추가
        quackers.add(quacker);
    }

    @Override
    public void quack() {
        //반복자(Iterator)패턴 사용
        //오리 리스트(quackers)를 이터레이터로 만든다.
        Iterator<QuackableInterfaceForCompoundPattern> iterator
                = quackers.iterator();

        while (iterator.hasNext()) {
            //이터레이터에 남은 원소가 없을 때까지 반복한다.
            QuackableInterfaceForCompoundPattern quacker = iterator.next();
            quacker.quack();
        }

    }
}

 

 

//DuckSimulator.java

package com.example.designpatternstudy.compoundPattern;

public class DuckSimulator {

    public static void initDuckSimul(){
        DuckSimulator simulator = new DuckSimulator();
        //simulate()메서드에 전달할 팩토리 생성(데코레이터 사용)
        AbstractDuckFactory duckFactory = new CountingDuckFactory();

        //파라미터 추가
        simulator.simulateAll(duckFactory);
    }

    //파라미터 추가
    void simulateAll(AbstractDuckFactory duckFactory){
        QuackableInterfaceForCompoundPattern redheadDuck = duckFactory.createRedheadDuck();
        QuackableInterfaceForCompoundPattern duckCall = duckFactory.createDuckCall();
        QuackableInterfaceForCompoundPattern rubberDuck = duckFactory.createRubberDuck();
        QuackableInterfaceForCompoundPattern gooseDuck = new GooseAdapter(new Goose());


        System.out.println("\n오리 시뮬레이션 게임: 무리 (+ 컴포지트)");

        Flock flockOfDucks = new Flock();
        flockOfDucks.add(redheadDuck);
        flockOfDucks.add(duckCall);
        flockOfDucks.add(rubberDuck);
        flockOfDucks.add(gooseDuck);


        //물오리만 들어가는 Flock객체(Composite객체) 생성
        Flock flockOfMallards = new Flock();

        //개별 물오리(mallard) 생성
        QuackableInterfaceForCompoundPattern mallardOne = duckFactory.createMallardDuck();
        QuackableInterfaceForCompoundPattern mallardTwo = duckFactory.createMallardDuck();
        QuackableInterfaceForCompoundPattern mallardThree = duckFactory.createMallardDuck();
        QuackableInterfaceForCompoundPattern mallardFour = duckFactory.createMallardDuck();

        //개별 물오리를 컴포지트 객체에 추가하기
        flockOfMallards.add(mallardOne);
        flockOfMallards.add(mallardTwo);
        flockOfMallards.add(mallardThree);
        flockOfMallards.add(mallardFour);

        //물오리 무리(flockOfMallards)를 아까 만든 오리 무리(flockOfDucks)에 넣는다.
        flockOfDucks.add(flockOfMallards);

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

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

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

    }
    //데코레이터도 QuackableInterfaceForCompoundPatter 인터페이스이다.
    void simulate(QuackableInterfaceForCompoundPattern duck){
        duck.quack();
    }


}
//
//오리 시뮬레이션 게임: 무리 (+ 컴포지트)
//
//오리 시뮬레이션 게임: 전체 무리
//꽥꽥
//꽉꽉
//삑삑
//끽끽
//꽥꽥
//꽥꽥
//꽥꽥
//꽥꽥
//
//오리 시뮬레이션 게임: 물오리 무리
//꽥꽥
//꽥꽥
//꽥꽥
//꽥꽥
//오리가 소리 낸 횟수:11 번

 

6. Observer Pattern (QuackObservable, Observable, Observer 생성) + QuackableInterfaceForCompoundPattern와 그 구현 클래스 전부 수정

  • 오리들(QuackableInterfaceForCompound)이 소리 냈을 때 바로 연락받고 싶어졌다. 따라서 옵저버 패턴을 사용하였다.
  • QuackObservable 인터페이스를 생성한다. 이 인터페이스는 옵저버를 등록하는 메소드인 registerObserver(Observer observer) 메소드와, 옵저버에게 연락을 돌리는 notifyObservers() 메소드를 가지고 있다.
  • 오리들(QuackableInterfaceForCompoundPattern)을 감시하고자 오리들로 하여금 QuackObservable을 확장하게 하였다.
  • QuackableInterfaceForCompoundPattern 인터페이스를 구현하는 클래스들이 QuackObservable의 메소드를 구현하도록 일일이 작업해야 했지만 다른 방식을 사용했다. 
  • 옵저버 등록 및 연락을 돌리는 코드를 캡슐화하여 갖는 Observable 클래스를 보조 클래스로 생성하는 것이다. Observable클래스는 QuackObservable 인터페이스를 구현함으로써 Quackable 인터페이스의 등록, 연락 메소드를 보유할 수 있다. 이렇게 하면 실제 코드는 Observable에 한 번만 써놓고 QuackObservable이 필요한 작업을 Observable에 전부 위임할 수 있다.
  • QuackableInterfaceForCompoundPattern인터페이스의 구현 클래스들로 하여금 Observable observable을 레퍼런스로 추가한다. 그리고 registerObserver()의 내부에서 observable에게 메소드 실행을 위임한다.
  • Observer 인터페이스를 생성한다. 이 인터페이스는 public void update(QuackObservable duck) 메소드를 보유한다.
  • Quackologist 클래스를 생성한다. 이 클래스가 Observer 인터페이스를 구현하여 옵저버의 역할을 하게 된다.

 

QuackObservable 인터페이스 생성

//QuackObservable.java

package com.example.designpatternstudy.compoundPattern;

//개별 객체의 동작을 감시하고자 할 때 사용
//여기서는 QuackableInterfaceForCompoundPattern을 의미한다.
public interface QuackObservable {

    //옵저버를 등록하는 메소드
    //Observer 인터페이스를 구현하는 객체라면 어떤 객체든 꽥꽥 소리 내는 걸 감시할 수 있다.
    //여기서는 QuackableInterfaceForCompoundPattern에서 구현한다.
    public void registerObserver(Observer observer);

    //옵저버에 연락을 돌리는 메소드
    public void notifyObservers();
}

 

QuackableInterfaceForCompoundPattern 수정

( QuackObservable인터페이스 확장)

//QuackableInterfaceForCompoundPattern.java

package com.example.designpatternstudy.compoundPattern;
//개별 객체(오리)를 감시하고 싶어서 이 인터페이스로하여금 Observable 클래스를 확장하도록 하였다.
public interface QuackableInterfaceForCompoundPattern extends QuackObservable {
    public void quack();
}

 

Observable 클래스 생성

(QuackObservable의 보조 클래스로서 QuackObservable클래스를 구현하여 등록과 연락 기능을 캡슐화 하여 보유한다. 따라서 QuackableInterfaceForCompoundPattern 인터페이스의 구현클래스들이 각각 메소드를 구현하지 않고도 Observable에게 구현을 위임할 수 있음. 물론 QuackableInterfaceForCompoundPattern 의 구현클래스들은 Observable을 참조로서 클래스에 추가해야 함.)

//Observable.java

package com.example.designpatternstudy.compoundPattern;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;



public class Observable implements QuackObservable{

    List<Observer> observers = new ArrayList<Observer>();
    QuackObservable duck;

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

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void notifyObservers() {
//        Iterator iterator = observers.iterator();
//        위 코드대로 하면 경고 발생 : Raw use of parameterized class 'Iterator'
        Iterator<Observer> iterator = observers.iterator();

        while (iterator.hasNext()){
            Observer observer = iterator.next();
            observer.update(duck);
        }
    }
}

 

 

QuackableInterfaceForCompoundPattern의 구현클래스들과 Observable의 결합.

(Observable을 레퍼런스로서 추가하고, 등록과 연락 메소드에서 observable에게 구현을 위임한다. 따라서 개별적으로 구현부를 작성해 줄 필요가 없다. 예시는 MallardDuck만 들었으며 다른 오리들도 똑같이 하면 된다.)

//MallardDuck.java
package com.example.designpatternstudy.compoundPattern;



//청둥오리
public class MallardDuck implements QuackableInterfaceForCompoundPattern {

    Observable observable;

    public MallardDuck(){
        observable = new Observable(this);
    }

    @Override
    public void quack() {
        System.out.println("꽥꽥");
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer observer) {
        observable.registerObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observable.notifyObservers();
    }
}

 

 

Observer 인터페이스 생성

(실질적으로 옵저버 역할을 할 Quackologist 클래스가 이 인터페이스를 구현한다.)

//Observer.java
package com.example.designpatternstudy.compoundPattern;

public interface Observer {
    public void update(QuackObservable duck);
}

 

Quackologist 클래스 생성

(실질적으로 옵저버 역할을 하는 클래스)

//Quackologist.java

package com.example.designpatternstudy.compoundPattern;

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

 

DuckSimulator 수정

//DuckSimulator.java

package com.example.designpatternstudy.compoundPattern;

public class DuckSimulator {

    public static void initDuckSimul(){
        DuckSimulator simulator = new DuckSimulator();
        //simulate()메서드에 전달할 팩토리 생성(데코레이터 사용)
        AbstractDuckFactory duckFactory = new CountingDuckFactory();

        //파라미터 추가
        simulator.simulateAll(duckFactory);
    }

    //파라미터 추가
    void simulateAll(AbstractDuckFactory duckFactory){
        QuackableInterfaceForCompoundPattern redheadDuck = duckFactory.createRedheadDuck();
        QuackableInterfaceForCompoundPattern duckCall = duckFactory.createDuckCall();
        QuackableInterfaceForCompoundPattern rubberDuck = duckFactory.createRubberDuck();
        QuackableInterfaceForCompoundPattern gooseDuck = new GooseAdapter(new Goose());


//        System.out.println("\n오리 시뮬레이션 게임: 무리 (+ 컴포지트)");

        Flock flockOfDucks = new Flock();
        flockOfDucks.add(redheadDuck);
        flockOfDucks.add(duckCall);
        flockOfDucks.add(rubberDuck);
        flockOfDucks.add(gooseDuck);


        //물오리만 들어가는 Flock객체(Composite객체) 생성
        Flock flockOfMallards = new Flock();

        //개별 물오리(mallard) 생성
        QuackableInterfaceForCompoundPattern mallardOne = duckFactory.createMallardDuck();
        QuackableInterfaceForCompoundPattern mallardTwo = duckFactory.createMallardDuck();
        QuackableInterfaceForCompoundPattern mallardThree = duckFactory.createMallardDuck();
        QuackableInterfaceForCompoundPattern mallardFour = duckFactory.createMallardDuck();

        //개별 물오리를 컴포지트 객체에 추가하기
        flockOfMallards.add(mallardOne);
        flockOfMallards.add(mallardTwo);
        flockOfMallards.add(mallardThree);
        flockOfMallards.add(mallardFour);

        //물오리 무리(flockOfMallards)를 아까 만든 오리 무리(flockOfDucks)에 넣는다.
        flockOfDucks.add(flockOfMallards);


        System.out.println("\n오리 시뮬레이션 게임 (+옵저버))");
        Quackologist quackologist = new Quackologist();

        flockOfDucks.registerObserver(quackologist);

        simulate(flockOfDucks);


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

    }
    //데코레이터도 QuackableInterfaceForCompoundPatter 인터페이스이다.
    void simulate(QuackableInterfaceForCompoundPattern duck){
        duck.quack();
    }

}

/*
꽥꽥
꽥꽥학자: com.example.designpatternstudy.compoundPattern.RedheadDuck@466276d8 가 방금 소리냈다.
꽉꽉
꽥꽥학자: com.example.designpatternstudy.compoundPattern.DuckCall@5ce8d869 가 방금 소리냈다.
삑삑
꽥꽥학자: com.example.designpatternstudy.compoundPattern.RubberDuck@27eedb64 가 방금 소리냈다.
끽끽
꽥꽥
꽥꽥학자: com.example.designpatternstudy.compoundPattern.MallardDuck@64c63c79 가 방금 소리냈다.
꽥꽥
꽥꽥학자: com.example.designpatternstudy.compoundPattern.MallardDuck@31c7528f 가 방금 소리냈다.
꽥꽥
꽥꽥학자: com.example.designpatternstudy.compoundPattern.MallardDuck@2b76ff4e 가 방금 소리냈다.
꽥꽥
꽥꽥학자: com.example.designpatternstudy.compoundPattern.MallardDuck@7a1234bf 가 방금 소리냈다.
오리가 소리 낸 횟수:7 번
*/

 

7. 클래스 다이어그램

 

Comments