본문 바로가기
디자인패턴/구조

[디자인패턴]데코레이트 패턴

by se0nghyun2 2023. 3. 15.
더보기

구조패턴 : 클래스나 객체를 상속과 합성을 통하여 더 큰 구조를 만드는 패턴

 

데코레이트 패턴

1. 데코이트(decorate)란 기본틀에 의미있게 꾸미다 라는 의미를 지닌다.

2. 상속과 합성을 통하여 더 큰 구조를 만드는 패턴

=>  타겟 클래스(기본틀)의 기능을 상속과 합성을 통하여 확장(꾸미는)하는 패턴 

즉, 다양하게 기능을 추가할 수 있다 라는 뜻이다.

 


예시로 아래와 같은 개발 요건이 존재한다.

1. 기본총이 존재
2. 해당 총에는 대용량 탄창이나 스코프 를 장착을 할 수 있다.
3. 기본총에 아무런 장비를  끼지 않을 수도 있고 모두 낄 수도 있다. 
4. 추후 기본 총에 개머리판, 수직 손잡이 를 달 수도 있다고 있다.

 

 

1. 데코레이트 패턴을 적용하지 않고 상속으로 해당 요건을 코딩

 

 - Gun클래스 : 부모 클래스이며 기본총을 의미

 - 스코프Gun 클래스 : 스코프가 장착된 총

 - 대용량탄창Gun 클래스 : 대탄이 장착된 총

 - 스코프와대용량탄창 클래스 : 스코프,대탄이 장착된 총

 

class Gun{
    void shoot(){
        System.out.println("탕탕 발사!");
    }
}

class 스코프Gun extends Gun{
    void shoot(){
        System.out.println("스코프 장착 완료");
        super.shoot();
    }
}

class 대용량탄창Gun extends Gun{
    void shoot(){
        System.out.println("대탄 장착 완료");
        super.shoot();
    }
}

class 대용량탄창과스코프Gun extends Gun{
    void shoot(){
        System.out.println("대탄 및 스코프 장착 완료");
        super.shoot();
    }
}

Gun클래스는 기본적인 총으로 발사 기능을 제공하며, 

스코프를 끼고 정밀조준이라던지 대용량 탄창을 끼고 총알 발사 수를 늘린다던지

추가적인 기능을 위해서 Gun을 상속받아 각 기능별 클래스들을 구현해뒀습니다.

구현된 클래스는 총 4개입니다.

 

그러나 문제점이 있습니다.

만약 개머리판, 수직 손잡이 를 달 수도 있다고 있다고 추가 요건이 들어왔습니다.

이러면 우리가 구현해야할 클래스는 몇개나 될까요?

스코프 O X

개머리판 O X

수직손잡이 O X

대용량 탄창 O X

각 경우의 수가 2개씩 되니  2*2*2*2 = 16 , 총 16개의 클래스를 생성해야 합니다. (정확히는 1개의 부모클래스, 15개의 서브 클래스) 

이를 데코레이트 패턴을 적용하여 해결해본다.

 

 

 

2. 데코레이트 패턴 적용 

1번과 다르게 하나의 기능을 수행하는 클래스를 구현하고

 

 

abstract class Gun{
    abstract void shoot();
}

class BasedGun extends Gun{
    void shoot(){
        System.out.println("탕탕탕");
    }
}
class DecorateGun extends Gun{
    private Gun gun;

    public DecorateGun(Gun gun){
        this.gun = gun;
    }

    @Override
    void shoot() {
        this.gun.shoot();
    }
}

class 대용량탄창Gun extends DecorateGun{
    public 대용량탄창Gun(Gun gun) {
        super(gun);
    }
    void shoot(){
        System.out.println("대탄 장착 완료");
        super.shoot();
    }
}

class 스코프Gun extends DecorateGun{
    public 스코프Gun(Gun gun) {
        super(gun);
    }
    void shoot(){
        System.out.println("스코프 장착 완료");
        super.shoot();
    }
}

public class Main {
    public static void main(String[] args) {
    	//객체의 조합
        Gun gun = new 스코프Gun(new 대용량탄창Gun(new BasedGun()));
        gun.shoot(); //스코프 + 대용량탄창 + 기본총
    }
}

이전엔 기능별로 클래스를 생성하여 구현했지만, 데코레이트 패턴을 적용하여 추가 기능을 행하는 클래스의 객체를 조합하여 추가 기능을 제공할 수 있다. 

이 때 ,나의 기능만 수행하는 클래스 1개만 생성하여 조합에 사용하면 된다.new ( new ( new ..)))


 

실사용 ( BufferedWriter)

JAVA에서 데코레이트 패턴 예시를 찾다보니 내가 실제 사용했던 부분에도 해당 패턴이 적용되고 있었다.

BufferWriter

public static void main(String[] args) throws IOException {
        BufferedWriter bw   = new BufferedWriter(new FileWriter(new File("asd")));

        bw.write("테스트 작성");

        bw.flush();
        bw.close();
    }

BufferedWriter는 FileWriter에 Buffer 추가해 disk IO을 줄여주는 역할을 한다.

 

BufferedWriter 클래스의 일부이다.

public class BufferedWriter extends Writer {

    private Writer out;
    ....
	
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }
    
    public BufferedWriter(Writer out, int sz) {
        super(out);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.out = out;
        cb = new char[sz];
        nChars = sz;
        nextChar = 0;
    }
    ...
    
    public void write(char cbuf[], int off, int len) throws IOException {
            synchronized (lock) {
                ensureOpen();
                if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
                    throw new IndexOutOfBoundsException();
                } else if (len == 0) {
                    return;
                }

                if (len >= nChars) {
                    flushBuffer();
                    out.write(cbuf, off, len);  /*해당 부분 */
                    return;
                }

                int b = off, t = off + len;
                while (b < t) {
                    int d = min(nChars - nextChar, t - b);
                    System.arraycopy(cbuf, b, cb, nextChar, d);
                    b += d;
                    nextChar += d;
                    if (nextChar >= nChars)
                        flushBuffer();
                }
            }
        }
 }

FilewWriter, BufferedWriter 모두  Writer를 상속받았으며,

BufferedWriter는 Writer interface의 구현체(new FileWriter) 를 받아서 out에 주입하고 있습니다.

BufferWriter의 write메소드 내부를 보면 out.write을 볼 수 있다.

BufferedWriter의 추가 기능을 수행하다가 결국 주입받은  FilleWriter의 write메소드를 수행한다.

이를 통해 데코레이트 패턴이 적용되었다고 볼 수 있다.

 

참고로 BufferedWriter는 DecoreateClass로 보면 된다.


데코레이트 패턴을 공부하먄면서 사용 시기를 아래와 같이 생각해보았다

-> 기능 확장이 필요할 시점에서 추가 기능이 많을 경우

 

 

 

 [참고]

[Design Pattern] Java에서 발견한 디자인패턴_Decorator Pattern (tistory.com)

Spring AOP - (1) 프록시 패턴, 데코레이터 패턴 (tistory.com)