04. 데코레이터 패턴

AlmondGood·2022년 7월 16일
0

디자인패턴

목록 보기
5/16
post-thumbnail

데코레이터 패턴(Decorator Pattern)

데코레이터 패턴이란, 객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있는 패턴을 말합니다.

용어가 살짝 어려운 것 같네요. 풀어보면
A라는 기능을 가진 프로그램에 A를 기반으로한 A-1 기능, A-2 기능, A-3기능, ... 등 "파생 기능을 만들고, 파생 기능 중에 원하는 기능들을 사용할 수 있게 하는 것입니다.

그리고 그 파생 기능들을 묶어주는 역할을 하는 것이 바로 "데코레이터"입니다.


바로 예시로 넘어가 보겠습니다.



데코레이터 패턴 구현

여러분들이 애플의 개발자가 되어 '시리'를 개발한다고 가정해 봅시다.
시리를 한 번 호출했을 때, 여러 가지의 동작동시에 실행할 수 있게 만들고 싶습니다.

처음에는 먼저 시리를 만들고, 각종 명령을 받을 준비를 합시다.

interface Siri {
	// 시리가 받을 명령들(하위 클래스에서 구현)
	void command();
}

시리를 만들었으니 이제 시리 호출 트리거를 만들어야겠죠?
이 기능이 위에서 말한 기반이 되는 기능이 되겠습니다.

// 시리야~
class Siriya implements Siri {
	@Override
    public void command() {    
    	System.out.println("네, 부르셨나요.");
    }
}

시리를 호출했으니 이제 여러 가지 동작을 만들어 보겠습니다.
하지만 그 전에, 동작들에 '시리야'를 상속시켜 구현한다면 객체 간 의존성이 높아지겠죠. 이를 묶어 줄 녀석이 필요합니다.

데코레이터입니다.

abstract class SiriDecorator implements Siri {

    // 명령을 받으려면 시리가 있어야겠죠. 
    // 시리를 선언해 줍니다.
    private Siri siri;

    // 시리가 어떠한 명령을 받습니다. 
    SiriDecorator(Siri siri) {  
    	this.siri = siri;
    }

    // 그 명령을 실행합니다.  
    @Override 
    public void command() {
    	siri.command();
    }
}

구체적인 명령 클래스들입니다.
"음악 틀어줘"와 "오늘 날씨 어때"입니다.

// "음악 틀어줘"
class PlayMusic extends SiriDecorator {

	//super는 부모 클래스를 뜻합니다.
    //super() 는 부모 클래스의 생성자를 호출합니다.
    PlayMusic(Siri siri) {  
        super(siri);
    }

    @Override
    public void command() { 
        super.command();
        playMusic();
    }

    private void playMusic() {
        System.out.println("네, 음악을 재생할게요.");
    }

}

// "오늘 날씨 어때"
class TodayWeather extends SiriDecorator {

    TodayWeather(Siri siri) {
        super(siri);
    }

    @Override
    public void command() {
        super.command();
        answerWeather();
    }

    private void answerWeather() {
        System.out.println("오늘 날씨는 맑음입니다.");
    }
}

메인 함수입니다.

public static void main(String[] args) {

        Siri siri = new Siriya();

        siri.command(); // "네, 부르셨나요."

		
		siri = new PlayMusic(new Siriya()); 

        siri.command();  // "네, 부르셨나요."
					     // "네, 음악을 재생할게요."


		
        siri = new TodayWeather(new PlayMusic(new Siriya())); 

        siri.command(); // "네, 부르셨나요."
					    // "네, 음악을 재생할게요."
					    // "오늘 날씨는 맑음입니다."
}

생성자가 여러 개 있는 것이 보이실텐데, 각 내부의 인자가 super(...) 를 통해
내부 인자를 먼저 실행되게 함으로써, 결과적으로 괄호 가장 안쪽부터 실행됩니다.

마지막 명령으로 설명하면

TodayWeather의 super.siri = new PlayMusic(new Siriya())
PlayMusic의 super.siri = new Siriya()

각 super.command()가 실행되면서
Siriya.command() - PlayMusic.playMusic() - TodayWeather.answerWeather() 순으로 실행됩니다.

결과적으로, "시리야"의 파생 기능들을 사용할 수도 있고, 사용하지 않을 수도 있고, 동시에 사용할 수도 있습니다.

이제 애플의 수석 개발자가 될 일만 남았네요.

데코레이터 패턴 in Java

사실 자바에 입문하면 데코레이터 패턴과 금방 마주하게 됩니다.

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

BufferedReader와 InputStreamReader는 Reader라는 클래스를 상속받고, super()를 통해 인자를 Reader의 생성자로 보내고, 이 Reader의 값을 받아 문자열을 얻어 버퍼에 저장합니다.
데코레이터를 통해 괄호 안 쪽부터 순차적으로 실행되는 것입니다.

자세한 내용은 링크를 첨부합니다.
BufferedReader에 관해 설명하면서 Scanner가 왜 느린지도 설명하고 있으니 읽어보시면 좋겠습니다.
https://st-lab.tistory.com/41



데코레이터 패턴의 장단점

장점

  • 기존 코드를 수정하지 않고도 데코레이터 패턴을 통해 행동을 확장시킬 수 있습니다.
  • 구성과 위임을 통해서 실행중에 새로운 행동을 추가할 수 있습니다.
    (구성 : 새 클래스에 기존 클래스의 객체를 생성하는 것, 형식이나 개념이 아닌 코드의 기능을 재사용하는 것임.)
    (위임 : 상속과 구성의 중간 개념. 어떤 행위를 위임 관계에 있는 객체에게 넘겨서 처리하는 것을 의미.)

단점

  • 의미없는 객체들이 너무 많이 추가될 수 있습니다.
  • 데코레이터를 너무 많이 사용하면 코드가 필요 이상으로 복잡해질 수 있습니다.



상속(extends) / 구현(implements)

  • 상속 : 클래스와 인터페이스에서 사용되며 is-a 관계를 나타냅니다.
    (is-a 관계 : 태생부터 무엇인가 타고난 기질 → I am human / class I extends Human)
  • 자바에서 클래스 다중 상속은 불가능합니다.
    (class I extends Human, Animal) (X)
    (class Run implements Exercise, Move) (O)
  • 클래스 내부의 코드를 공유합니다. (멤버 변수, 메소드)
  • 구현 : 인터페이스에서 사용되며 can-do 관계를 나타냅니다.
    (can-do 관계 : 배워서 할 수 있는 행동 → I can swim / class I implements Swimming)
  • 인터페이스는 다중 상속, 구현이 가능합니다.
    (interface Run extends Exercise, Move) (O)
    (interface Run implements Exercise, Move) (O)
  • 인터페이스 내부의 멤버 변수는 공유하지만 메소드는 직접 구현해야 합니다.

https://ryulib.tistory.com/76



참고자료

https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html

https://gregorio78.tistory.com/?page=59

https://st-lab.tistory.com/41

https://ryulib.tistory.com/76

https://m.blog.naver.com/typeofb/221619786248

profile
Zero-Base to Solid-Base

0개의 댓글