Decorator는 말 그대로 '장식하는 사람'이다. 프로그래밍에서 장식? 일단 아래 사진을 보자.
필자는 커피에 대해 무지깽이지만, 에스프레소에 물 태우면 아메리카노가 된다는 정도는 안다. 거기에 우유태우면 '라떼', 또 거기에 초콜릿 넣으면 '모카', 또 거기에 카라멜 넣으면 카라멜모카라떼? 또 거기에...
어쨌든 에스프레소 베이스만 있으면, 대충 때려붙여서 뭔가를 만들 수 있다. 그리고 이 방식이 바로 데커레이터다. 기본 베이스에 여러 장식품을 추가해 나가는 패턴. 프로그래밍에서도 필요한 스킬이다.
만일, 위의 (프로그래밍)커피전문점에서 데커레이터를 사용하지 않는다면 어떻게 될까?
Espresso()
Americano()
Cappucino()
Mocha()
Macchiato()
Latte()
...
..
(데커레이터가 없다면)커피 종류가 추가될 때마다 클래스가 추가되며, 클래스가 넘쳐나게 된다. 반면, 데커레이터를 사용하면 커피 재료와 관련된 클래스만 있으면 된다.
1 인터페이스 생성
데커레이터 역시 추상클래스부터 시작한다.
class AbstractOperation {
constructor(){
this.name = null;
}
operate() {
throw new Error('code this area');
}
setName(name) {
this.name = name;
}
toString(){
return `AbstractOperation`;
}
}
export default AbstractOperation;
|
[AbstractOperation.js]
Decorator인터페이스 클래스와 원재료(에스프레소) 클래스 모두 위의 Operation추상 클래스의 'operate메서드'를 사용(구현)해야 한다.
모든 커피의 핵심 재료인 'Espresso'클래스(메인 재료)의 인터페이스는 아래와 같다.
import AbstractOperation from "./AbstractOperation.js";
class Espresso extends AbstractOperation {
constructor() {
super();
this.beans = null;
}
operate() {
return `베이스 에스프레소`;
}
setBeans(bean) {
this.beans = bean;
}
toString() {
return `Espresso`
}
}
export default Espresso
|
[Espresso.js]
iDecorator.js는 커피제작에 사용되는 모든 재료(Espresso제외)의 인터페이스다. 코드는 대략 아래와 같이 생성해준다.
import AbstractOperation from "./AbstractOperation.js";
class iDecorator extends AbstractOperation {
constructor() {
super();
this.exp = null;
this.qyt = null;
}
setExp(exp, qyt) {
this.qyt = qyt;
this.exp = exp;
}
toString() {
return `iDecorator`;
}
}
export default iDecorator;
|
[iDecorator.js]
2 Decorator 구현
이제 커피를 꾸미는 데에 사용되는 여러 장식품(식재료)들을 본격적으로 생성해보자.
import iDecorator from './iDecorator.js';
class Milk extends iDecorator {
constructor() {
super();
}
operate() {
return `\n+${this.exp.operate()}, \n +우유, ${this.qyt}`;
}
toString() {
return `Milk`;
}
}
export default Milk;
|
[Milk.js]
데커레이터 구현 클래스에서 핵심은 operate메서드 내부의 this.exp.operate()이다. 해당 부분은 마치 함수를 인자로 받는 함수와 같다. operate()라는 공통의 메서드를 통해 함수의 실행 부분을 인자 내 함수의 실행 부분으로 계속 넘기는 식이다.
import iDecorator from './iDecorator.js';
class Chocolate extends iDecorator {
constructor() {
super();
}
operate() {
return `${this.exp.operate()}, \n +초코렛: ${this.qyt}`;
}
toString() {
return `Chocolate`;
}
}
export default Chocolate;
|
[Chocolate.js]
우유에 이어 초콜릿과 크림과 같은 재료도 데커레이터 인터페이스로 구현해준다.
import iDecorator from './iDecorator.js';
class Cream extends iDecorator {
constructor() {
super();
}
operate() {
return `${this.exp.operate()}, \n +휘핑크림, ${this.qyt}`;
}
toString() {
return `Cream`;
}
}
export default Cream;
|
[Cream.js]
3 조합 클래스
장식 재료(iDecorator.js 인터페이스 구현)와 메인재료(AbstractOperation 구현)를 모두 생성했다면, 이를 조합해주는 주방장이 필요하다. 한편, 커피전문점에서는 바리스타가 그 역할이다.
export default class Barista {
constructor() {
this.src = null;
}
make() {
return `${this.src.operate()}`;
}
setSrc(src) {
this.src = src;
}
toString() {
return `Barista`;
}
}
|
[Barista.js]
바리스타 클래스는 메인재료와 장식재료를 섞는 역할을 한다.
4 실행
client.html에서 위의 모든 클래스를 불러온 후에 조합(make메서드 실행)해보자.
<script type="module">
import Espresso from './Esopresso.js';
import Chocolate from './Chocolate.js';
import Cream from './Cream.js';
import Milk from './Milk.js';
import Barista from './Barista.js';
let espresso = new Espresso();
let choco = new Chocolate();
let cream = new Cream();
let milk = new Milk();
let barista = new Barista();
milk.setExp(espresso, '80g');
choco.setExp(milk, '30g');
cream.setExp(choco, '20g');
let src = cream;
// let src = milk(cream(choco(espresso, '30g'), '20g'), '50g');
barista.setSrc( src );
let cafemocha = barista.make();
console.log( cafemocha );
</script>
|
[client.html]
위와 같이 데커레이터를 사용한다면, 여러가지 커피 클래스가 필요없다. 가령, 카페라떼의 경우에는 new Cafelatte()라는 클래스를 제작할 필요가 없다. 아래와 같이 코드 몇줄 추가하면 된다.
'코드 스터디' 카테고리의 다른 글
「WebPack 개발」 1 웹펙 세팅 & 웹서버 실행 (0) | 2023.04.03 |
---|---|
마이크로 프론트엔드 개발 (0) | 2023.04.01 |
옵저버 패턴 (0) | 2022.12.08 |
for루프 비동기 구문을 프로미스로 해결하기 (0) | 2022.06.01 |
Promise.all & .map() 조합 (0) | 2022.02.04 |
댓글