본문 바로가기
Development, Architecture/Design Pattern

[디자인 패턴] Decorator Pattern 데코레이터 패턴

by ggyongi 2021. 12. 1.
반응형

Decorator Pattern

Introduce

천재 과학자 이씨는 아톰을 만들었습니다.
아톰의 초기 버전은 로봇의 기본 동작만 수행합니다.
앞으로 새로운 버전이 나올 때마다 아톰의 기능은 추가될 수 있습니다.
기능이 반드시 추가만 되는 것은 아니고 특정 기능을 뺄 수도 있습니다.
즉, 여러 기능을 자유롭게 조합시켜 아톰에게 탑재시킬 수 있어야 합니다.
이럴때 데코레이터 패턴이 사용될 수 있습니다.

Definition

데코레이터 패턴은 객체에 추가적인 요건을 동적으로 첨가합니다. 쉽게 말해 장식과 같이 하나의 객체에 여러 기능을 꾸며줍니다.
데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공합니다.

Structure

image

Source Code

가장 먼저 컴포넌트 인터페이스를 만들어줍니다. 구현체가 없는 operate() 메서드를 갖고 있습니다.


public interface Robot {
    public void operate();
}

이 컴포넌트 인터페이스를 구현하는 콘크리트컴포넌트 클래스를 만들어줍니다. operate()를 구현합니다.


public class DefaultRobot implements Robot {

    public void operate() {
        System.out.print("Basic");
    }

}

그리고 데코레이터 클래스를 작성합니다.
여기에서도 operate() 메서드를 구현하게 됩니다.


public class RobotDecorator implements Robot {

    private Robot robot;

    public RobotDecorator(Robot robot) {
        this.robot = robot; 
    }

    public void operate() {
        robot.operate();
    }
}

그리고 데코레이터 클래스를 상속받아 다양한 기능을 가진 클래스들을 작성해줍니다.
구조가 조금 어렵게 느껴질 수 있는데, 생성자의 인자로 컴포넌트형을 받는 이유는 이렇게 해야 장식을 동적으로 연결시켜줄 수 있기 때문입니다.
테스트 실행 코드를 보면 조금 감이 올 수도 있습니다. 하지만 그래도 어려움


public class Fly extends RobotDecorator {

    public Fly(Robot robot){
        super(robot); 
    }

    public void operate() {
        super.operate();
        System.out.print(" + Fly");
    }

}

public class Attack extends RobotDecorator {

    public Attack(Robot robot) {
        super(robot);
    }

    public void operate() {
        super.operate();
        System.out.print(" + Attack");
    }
}

테스트는 아래와 같이 진행됩니다.

import javax.print.attribute.standard.PrinterInfo;

public class PatternTest {

    public static void main(String[] args) {

        System.out.println("Atom verson1");
        Robot atom_v1 = new DefaultRobot();
        atom_v1.operate();

        System.out.println("\n\nAtom verson2");
        Robot atom_v2 = new Fly(new DefaultRobot());    
        atom_v2.operate();


        System.out.println("\n\nAtom verson3");
        Robot atom_v3 = new Attack(new Fly(new DefaultRobot()));
        atom_v3.operate();


    }

}

기능이 동적으로 추가되는 것을 볼 수 있습니다.

Atom verson1
Basic

Atom verson2
Basic + Fly

Atom verson3
Basic + Fly + Attack

처음에 이 방식이 도통 이해가 되지 않았었는데,
위의 atom_v3를 보면 우선 new Attack(~)으로 인스턴스를 생성하게 되고 그 인자로 new Fly()가 들어갑니다.
Attack의 생성자를 보면 이 Fly()를 super의 robot에 할당하게 됩니다. 즉, 데코레이터 클래스에서 작성했었던 private Robot robot 이거!!
그리고 atom_v3.operate()를 하게되면 Attack 클래스의 operate()를 보면 super.operate()를 먼저 실행하게 되어 있습니다.
즉 상위 클래스인 데코레이터 클래스의 operate()를 먼저 실행하게 되는데 또 보면 데코레이터의 operate()는 robot.operate()를 하도록 되어 있습니다.
근데 이 robot에선 뭐가 있다? 우리가 아까 할당해줬던 Fly()가 할당되어 있죠. 그래서 이때 Fly의 operate()부터 실행되는 것입니다.
그렇게 재귀 구조로 진행을 하면 결국 감싸준 순서대로 기능이 점점 추가되는 것을 출력을 통해 보실 수 있습니다. 어렵다 어려워!!

 

비전공자 네카라 신입 취업 노하우

시행착오 끝에 얻어낸 취업 노하우가 모두 담긴 전자책!

kmong.com

댓글