정의 : 객체에 추가요소를 동적으로 더할 수 있도록 하는 디자인 패턴. 데코레이터를 사용하면 서브클래스를 만들 때 보다 훨씬 유연하기 기능을 확장할 수 있다
이 패턴을 공부하고 나서 들었던 생각은 내가 디아블로에서 아이템들의 상호작용으로 공격력이 판단되는데 그런걸 프로그램으로 만들 때 주로 쓰겠구나 라는 생각을 해서 아래와 같이 예시를 만들어 보았다.
가상 상태
주니어 개발자 준은 디아블로 게임을 제작하는 개발자다. 어느날 기획서가 준에게 날아왔다
캐릭터가 마법사와 악마사냥꾼이 있음
마법사는 기본 공격력이 10, 악마사냥꾼은 기본 공격력이 20이다
아이템이 갑옷, 무기, 반지가 있다
캐릭터는 공격력을 알려주는 함수와 캐릭터 외형을 보여주는 함수가 있어야 한다
갑옷, 무기, 반지에는 이팩트 함수가 추가되고 공격력도 높여준다.
허름한 갑옷을 입으면 공격력이 떨어진다.
데커레이터 페턴을 몰랐던 개발자 준은
이렇게 디자인을해서 개발을 하려 했더니
마법사인데 무기만 든 캐릭터
악마사냥꾼인데 무기에 갑옷만 입은 사람
마법사인데 무기, 좋은 갑옷, 반지만 입은 사람
이런 경우의 수를 다 만드려고 하니까
이건 아니라는 생각이 들어 시니어 개발자인 신에게 다가가 조언을 듣는다.
신 : "뭔가 계속 더해가는거 같은데, 데커레이터 패턴을 쓰면 쉽게 해결할 수 있을 것 같은데요??"
!!!!!
개발자 신을 신뢰하는 준은 데커레이터 패턴을 공부해 아래와 같이 구조를 변경하고 프로젝트를 진행했다.
먼저 추상화 클레스로 캐릭터를 만들자
공격력은 캐릭터 마다 다르고 캐릭터 형상은 캐릭터마다 다르니까
아래와 같이 추상메소드를 만든다
abstract class 캐릭터 {
protected 공격력: number = 0;
공격() {
return this.공격력;
}
abstract 캐릭터(): string;
}
그리고 마법사와 악마 사냥꾼을 캐릭터를 상속받아서 만들어 준다
class 마법사 extends 캐릭터 {
constructor() {
super();
this.공격력 = 10;
}
캐릭터() {
return "머리가 긴 캐릭터. 사운드 : 비전력이 부조카당";
}
}
class 악마사냥꾼 extends 캐릭터 {
constructor() {
super();
this.공격력 = 20;
}
캐릭터() {
return "단발 머리의 캐릭터 사운드 : 넌 선택할 수 있어. 사냥 당하거나, 사냥 하거나.";
}
}
그리고 캐릭터가 장착할 아이템을 위해서 캐릭터 데코레이서 추상 클래스를 만들고 타입을 맞추는 방법으로 상속을 이용한다.
그리고 선택된 캐릭터를 composition으로 만들어준다.
abstract class 캐릭터_데코레이터 extends 캐릭터 {
constructor(protected readonly 선택된_캐릭터: 캐릭터) {
super();
}
abstract 공격(): number;
abstract 캐릭터(): string;
}
이제 아이템들을 구현해주자!
class 갑옷 extends 캐릭터_데코레이터 {
constructor(protected readonly 선택된_캐릭터: 캐릭터) {
super(선택된_캐릭터);
}
캐릭터() {
return this.선택된_캐릭터.캐릭터() + ", 모션 : 삐까번쩍 갑옷장착!";
}
공격() {
return this.선택된_캐릭터.공격() + 20;
}
}
class 허름한_갑옷 extends 캐릭터_데코레이터 {
constructor(protected readonly 선택된_캐릭터: 캐릭터) {
super(선택된_캐릭터);
}
캐릭터() {
return this.선택된_캐릭터.캐릭터() + ", 모션 : 방어력에 공격 움직임이 둔하다";
}
공격() {
return this.선택된_캐릭터.공격() - 5;
}
}
class 무기 extends 캐릭터_데코레이터 {
constructor(protected readonly 선택된_캐릭터: 캐릭터) {
super(선택된_캐릭터);
}
캐릭터() {
return this.선택된_캐릭터.캐릭터() + ", 모션 : 탈라샤의 무기 빛이 난다";
}
공격() {
return this.선택된_캐릭터.공격() + 50;
}
}
class 반지 extends 캐릭터_데코레이터 {
constructor(protected readonly 선택된_캐릭터: 캐릭터) {
super(선택된_캐릭터);
}
캐릭터() {
return (
this.선택된_캐릭터.캐릭터() + ", 모션 : 최고의 반지! 주의에 빛이 감싼다"
);
}
공격() {
return this.선택된_캐릭터.공격() + 100;
}
}
게임 세계에서 대균열 100단을 모든 2인 팟이다
여긴 모든게 최고인 사람들이 모여있는 곳!
대균열 캐릭터를 알아보자
const 대균열100단 = () => {
// 마법사
const 풀탬법사 = new 갑옷(new 반지(new 무기(new 마법사())));
const 풀탬악마사냥꾼 = new 갑옷(new 반지(new 무기(new 악마사냥꾼())));
[풀탬법사, 풀탬악마사냥꾼].forEach((캐릭터) => {
console.log("공격력을 알아보자");
console.log(캐릭터.공격());
console.log("캐릭터가 외관");
console.log(캐릭터.캐릭터());
});
};
대균열100단();
공격력을 알아보자
180
캐릭터가 외관
머리가 긴 캐릭터
사운드 : 비전력이 부조카당,
모션 : 탈라샤의 무기 빛이 난다,
모션 : 최고의 반지! 주의에 빛이 감싼다,
모션 : 삐까번쩍 갑옷장착!
공격력을 알아보자
190
캐릭터가 외관
단발 머리의 캐릭터.
사운드 : 넌 선택할 수 있어 사냥 당하거나, 사냥 하거나.,
모션 : 탈라샤의 무기 빛이 난다,
모션 : 최고의 반지! 주의에 빛이 감싼다,
모션 : 삐까번쩍 갑옷장착!
버스타려고 캐릭터 두명이 균열에 남아있다.
const 균열 = () => {
const 버스타는법사 = new 갑옷(new 무기(new 마법사()));
const 버스타는악마사냥꾼 = new 허름한_갑옷(new 마법사());
[버스타는법사, 버스타는악마사냥꾼].forEach((캐릭터) => {
console.log("공격력을 알아보자");
console.log(캐릭터.공격());
console.log("캐릭터가 외관");
console.log(캐릭터.캐릭터());
});
};
균열();
공격력을 알아보자
80
캐릭터가 외관
머리가 긴 캐릭터
사운드 : 비전력이 부조카당,
모션 : 탈라샤의 무기 빛이 난다,
모션 : 삐까번쩍 갑옷장착!
공격력을 알아보자
15
캐릭터가 외관
단발 머리의 캐릭터
사운드 : 넌 선택할 수 있어. 사냥 당하거나, 사냥 하거나.,
모션 : 방어력에 몰빵
개발자 준은 다양한 아이템을 가진 캐릭터를 디아블로 세계를 구현했다!!
위까지 내가 만든 예시이다.
풀탬법사를 기준으로
const 풀탬법사 = new 갑옷(new 반지(new 무기(new 마법사())));
체이닝해가면서 값들을 축척해 나가는걸 볼 수 있다.
다시 정의를 살펴보면
정의 : 객체에 추가요소를 동적으로 더할 수 있도록 하는 디자인 패턴. 데코레이터를 사용하면 서브클래스를 만들 때 보다 훨씬 유연하기 기능을 확장할 수 있다
-> 여기에 개인적인 정의를 해보면 조합을 다양하게 할 수 있고 조합들간 원하는 값을 축적시킬 때 사용는 패턴
이렇게 정의될 것 같다.
개인이 직접 예시를 만들어보면서 느낀거여서 더 기능을 확장해서 쓸 수 있는데 놓칠 수 도 있을 것 같다고 판단이 든다.
혹시 이글을 보시고 첨언하고 싶으시면 제발 해주세요..