구조패턴 : 클래스나 객체를 상속과 합성을 통하여 더 큰 구조를 만드는 패턴
데코레이트 패턴
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)
'디자인패턴 > 구조' 카테고리의 다른 글
[디자인패턴] 브릿지 패턴 (0) | 2023.04.28 |
---|---|
플라이웨이트(flgweight) 패턴 (0) | 2023.04.05 |
[디자인패턴] 어댑터 패턴 (0) | 2023.03.23 |
[디자인패턴]컴포지트(composite) 패턴 (0) | 2023.03.22 |