안녕하세요. 이번 시간부터 자바스크립트 디자인 패턴에 대해 심도있게 다뤄보겠습니다. 예전에 디자인패턴을 잠깐 소개했습니다. 다시 설명하자면, 디자인 패턴은 어떤 문제에 대한 해법입니다. 어떠한 문제를 해결할 수 있는 패턴을 알고 있으면 문제가 반복적으로 발생할 지라도 같은 방법으로 해결할 수 있습니다.
첫 번째는 추상 팩토리 패턴입니다. 쉽게 설명하기 위해서 재밌는 이야기를 하나 들려드리겠습니다.(실제 역사와 다소 상이할 수 있습니다) 때는 기원 후 66년, 네로 황제의 집권 시기였습니다. 로마 대화재 이후 네로 황제에 대한 불만이 로마 제국 전역에서 터져나오고 있었죠.
스페인 총독 갈바, 포르투갈 총독 오토, 프랑스 총독 빈덱스, 아프리카 총독 비텔리우스, 독일 총독 루푸스, 시리아 총독이자 전쟁 영웅인 코르불로 등이 호시탐탐 기회를 노리고 있었습니다. (지명은 알기 쉽게 바꿨습니다. 저 나라들이 로마 제국 때는 모두 한 국가였습니다...)
67년 전쟁 영웅으로 가장 인기가 많았던 코르불로가 반란 혐의로 처형당했습니다. 이 때부터 전국에서 반란이 일어나게 됩니다. 68년부터 69년까지는 네로를 포함해 5명의 황제가 등장합니다. 특히 69년을 역사에서는 네 황제의 해라고 부릅니다.
이 시기를 자바스크립트로 구현해보겠습니다. 일단 로마의 시스템을 객체로 만들어야겠죠? 황제도 있고, 총독도 있고, 장군도 있고, 원로원에 있는 정치인도 있을 수 있겠네요. 별의별 사람들이 다 있다보니 한 번에 처리하기 힘들어 보입니다. 따로 하나씩 만들기엔 코드가 중복되는 게 많이 나올 것 같고요. 이럴 때 추상 팩토리 패턴이 유용합니다. 일단 유형을 먼저 등록받고, 알아서 구현하도록 하는 겁니다. 대신 모든 유형의 사람은 서로를 공격할 수 있어야합니다.
var abstractCharacterFactory = (function() {
var jobs = {};
return {
addJob: function(job, Character) {
if (Character.prototype.attack) { // attack 메소드가 있어야만 등록 가능
jobs[job] = Character;
}
},
create: function(job, options) { // 등록한 직업을 바탕으로 실제 객체 생성
var Character = jobs[job];
return (Character ? new Character(options) : null);
}
};
})();
위의 코드를 잘 보면, 일단 addJob으로 다양한 유형의 직업을 받을 수 있습니다. 그러고나서 create 메소드로 실제 객체를 생성하는 겁니다. 일단 직업부터 만들어보죠. 간단하게 황제와 총독을 만들겠습니다.
var Emperor = (function() {
function Emperor(options) {
this.name = options.name;
}
Emperor.prototype.attack = function(target) {
console.log(this.name + '가 ' + target + '을 공격합니다');
};
Emperor.prototype.proclaim = function() {
console.log(this.name + '가 스스로를 황제라고 칭했습니다');
};
return Emperor;
})();
var Governor = (function() {
function Governor(options) {
this.name = options.name;
}
Governor.prototype.attack = function(target) {
console.log(this.name + '가 ' + target.name + '을 공격합니다');
};
Governor.prototype.betray = function() {
console.log(this.name + '가 황제를 배신합니다');
};
return Governor;
})();
직업을 만들었으니 추상 팩토리에 등록하고, 실제 객체도 만들어봅시다. 실제로 코딩을 할 때는 반대 순서로 만드는 게 좋습니다. 먼저 결과를 생각하고 역순으로 구현해나가는 겁니다.
abstractCharacterFactory.addJob('emperor', Emperor);
abstractCharacterFactory.addJob('governor', Governor);
var nero = abstractCharacterFactory.create('emperor', { name: 'Nero' });
var vindex = abstractCharacterFactory.create('governor', { name: 'Vindex' });
var galba = abstractCharacterFactory.create('governor', { name: 'Galba' });
var otho = abstractCharacterFactory.create('governor', { name: 'otho' });
var vitellius = abstractCharacterFactory.create('governor', { name: 'vitellius' });
var rufus = abstractCharacterFactory.create('governor', { name: 'rufus' });
팩토리라고 이름지어진 것은 공장에서 찍어내듯 객체를 생성할 수 있기 때문입니다. emperor나 governor 외에도 general, senator 등의 직업을 더 추가할 수 있습니다. 그냥 addJob 메소드만 호출하면 되니까요. 이제 네로에 대항해서 빈덱스, 갈바, 오토, 비텔리우스 총독이 반란을 일으킵니다.
vindex.betray(); // vindex가 황제를 배신합니다.
galba.betray(); // galba가 황제를 배신합니다.
otho.betray(); // otho가 황제를 배신합니다.
vitellius.betray(); // vitellius가 황제를 배신합니다.
정리하자면 추상 팩토리 패턴은 하나의 팩토리로 여러 종류의 팩토리(위의 예에서는 황제와 총독)를 동시에 운영할 수 있습니다. 다음 시간에는 객체를 만드는 다른 방법인 빌더 패턴에 대해서 알아보겠습니다.
// AS-IS
console.log(this.name + '가 ' + target+ '을 공격합니다');
nero.attack(vindex); // Nero가 [object Object]을 공격합니다
// TO-BE
console.log(this.name + '가 ' + target.name + '을 공격합니다');
nero.attack(vindex) // Nero가 Vindex을 공격합니다