관리 메뉴

bright jazz music

Compound Pattern: 2. Decorator 패턴 추가 본문

Design Pattern/Compound Pattern

Compound Pattern: 2. Decorator 패턴 추가

bright jazz music 2022. 10. 14. 22:32

갑자기 오리가 소리낸 횟수를 세는 기능을 오리 클래스 수정 없이 추가하고 싶어졌다.

이 때 데코레이터 패턴을 사용할 수 있다.

 

데코레이터 패턴은 기존 기능의 수정 없이 새로운 기능을 추가하고 싶을 때 사용할 수 있다. 사실상 어댑터 패턴과 유사하다. 어댑터 패턴은 호환에 좀 더 중점을 준다는 점이 다른 것 같다.

 

데코레이터 패턴은 데코레이터 클래스를 만들어서 기존 객체들을 데코레이터로 감싸면 된다. 이렇게 하면 Duck코드는 건드리지 않아도 된다.

 

1. 데코레이터 생성

//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;
    }

}

 

2. 시뮬레이터에 데코레이터 패턴 사용

//DuckSimulator.java

package com.example.designpatternstudy.compoundPattern;

public class DuckSimulator {

    public static void initDuckSimul(){
        DuckSimulator simulator = new DuckSimulator();
        simulator.simulate();
    }

    void simulateAll(){
        //인터페이스를 새로 생성할 때마다 새로운 데코레이터로 감싼다.
        QuackableInterfaceForCompoundPattern mallardDuck
                = new QuackCountingDecorator(new MallardDuck());

        QuackableInterfaceForCompoundPattern redheadDuck
                = new QuackCountingDecorator(new RedheadDuck());

        QuackableInterfaceForCompoundPattern duckCall
                = new QuackCountingDecorator(new DuckCall());

        QuackableInterfaceForCompoundPattern rubberDuck
                = new QuackCountingDecorator(new RubberDuck());
        //GooseAdapter를 사용해서 오리가 됨
        QuackableInterfaceForCompoundPattern gooseDuck = new GooseAdapter(new Goose());

        //GooseAdapter오리도 데코레이터 사용
        QuackableInterfaceForCompoundPattern gooseDuckDeco
                = new QuackCountingDecorator(new GooseAdapter(new Goose()));



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

        simulate(mallardDuck); //1
        simulate(redheadDuck); //2
        simulate(duckCall); //3
        simulate(rubberDuck); //4
        //거위 시뮬레이트트
       simulate(gooseDuck);
       simulate(gooseDuckDeco);//5

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

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


}

 

3. 테스트

package com.example.designpatternstudy;

import com.example.designpatternstudy.compoundPattern.DuckSimulator;
import org.junit.jupiter.api.Test;

public class CompoundPatternTests {


    @Test
    public void duckSimulTest(){

//        DuckSimulator duckSimulator = new DuckSimulator();
//        duckSimulator.initDuckSimul();
// 이렇게 했더니 아래와 같은 경고 발생
//        Static member 'com.example.designpatternstudy.compoundPattern.DuckSimulator.initDuckSimul()' accessed via instance reference
//        Inspection info: Reports references to static methods and fields via a class instance rather than the class itself.
//        Even though referring to static members via instance variables is allowed by The Java Language Specification, this makes the code confusing as the reader may think that the result of the method depends on the instance.
//                The quick-fix replaces the instance variable with the class name.
//        Example:
//        String s1 = s.valueOf(0);
//        After the quick-fix is applied:
//        String s = String.valueOf(0);
//        클래스의 인스턴스를 통해 static 멤버(여기서는 initDuciSimul())에 접근하는 것도 가능하다. 그러나 이는 코드를 헷갈리게 만든다.
//        독자는 해당 메소드의 결과가 객체에 의존적이라고 생각할 수도 있다.
//        ==> 해당 멤버가 static이라면 객체 생성하여 접근할 필요 없이 그냥 클래스로 접근하라는 뜻.

        DuckSimulator.initDuckSimul();
  
        
        //오리 시뮬레이션 게임 (+ 데코레이터)
        //꽥꽥
        //꽥꽥
        //꽉꽉
        //삑삑
        //끽끽
        //끽끽
        //오리가 소리 낸 횟수:5 번
    }
}


// 6번 출력되었지만 집계는 5번이 된다. 왜냐하면 gooseDuck는 데코레이터로 감싸지 않은 객체이기 때문이다.
// 반면 gooseDuckDeco는 어댑터로 감싼 Goose클래스를 다시 한 번 데코레이터로 감쌌기 때문에 집계되었다.

 

Comments