-
TIL_200921(GoF pattern2 & cheat sheet)Today I Learned 2020. 9. 21. 23:53
3. Behavioral Design Patterns - 객체 간의 통신을 개선합니다.
- Chain of Responsibility Pattern
더보기느슨하게 결합 된 객체의 체인을 제공하는 동작 디자인 패턴입니다.
책임 패턴의 좋은 예는 일련의 중첩된 DOM 요소를 통해 전파되는 DOM의 이벤트 버블링입니다.
그 중 하나에 이벤트를 수신하고 그에 대해 작동하기 위해 연결된 "이벤트 리스너"가 있을 수 있습니다.
class CumulativeSum { constructor(intialValue = 0) { this.sum = intialValue; } add(value) { this.sum += value; return this; } } // usage const sum1 = new CumulativeSum(); console.log(sum1.add(10).add(2).add(50).sum); // 62 const sum2 = new CumulativeSum(10); console.log(sum2.add(10).add(20).add(5).sum); // 45
- Command Pattern
더보기동작이나 작업을 객체로 캡슐화하는 것을 목표로 하는 패턴입니다.
작업을 요청하거나 메서드를 호출하는 객체를 분리하여 시스템과 클래스의 느슨한 결합을 허용합니다.
만약에 Redux 유저라면 이 패턴에 익숙할 것입니다.
class SpecialMath { constructor(num) { this._num = num; } square() { return this._num ** 2; } cube() { return this._num ** 3; } squareRoot() { return Math.sqrt(this._num); } } class Command { constructor(subject) { this._subject = subject; this.commandsExecuted = []; } execute(command) { this.commandsExecuted.push(command); return this._subject[command](); } } // usage const x = new Command(new SpecialMath(5)); x.execute('square'); x.execute('cube');
- Iterator Pattern
더보기기본 표현을 노출하지 않고 객체 요소에 순차적으로 접근하는 방법을 제공하는 패턴입니다.
Iterator는 끝에 도달 할 때까지 next()를 호출하여 한 번에 하나씩 순서가 지정된 값 집합을 단계별로 실행하는 동작을 가지고 있습니다. ES6에 Iterator 및 Generators가 도입되면서 패턴 구현이 간단해졌습니다.
// using Iterator class IteratorClass { constructor(data) { this.index = 0; this.data = data; } [Symbol.iterator]() { return { next: () => { if (this.index < this.data.length) { return { value: this.data[this.index++], done: false }; } else { this.index = 0; // to reset iteration status return { done: true }; } }, }; } } // using Generator function* iteratorUsingGenerator(collection) { var nextIndex = 0; while (nextIndex < collection.length) { yield collection[nextIndex++]; } } // usage const gen = iteratorUsingGenerator(['Hi', 'Hello', 'Bye']); console.log(gen.next().value); // 'Hi' console.log(gen.next().value); // 'Hello' console.log(gen.next().value); // 'Bye'
- Mediator Pattern
더보기객체 집합이 서로 상호 작용하는 방식을 캡슐화하는 동작 패턴입니다.
느슨한 결합을 촉진하여 객체가 서로를 명시적으로 참조하지 못하도록하여 객체 그룹에 대한 중앙 권한을 제공합니다.
예를 보면 TrafficTower와 Airplane의 register 메서드로 느슨하게 결합합니다.
class TrafficTower { constructor() { this._airplanes = []; } register(airplane) { this._airplanes.push(airplane); airplane.register(this); } requestCoordinates(airplane) { return this._airplanes.filter(plane => airplane !== plane).map(plane => plane.coordinates); } } class Airplane { constructor(coordinates) { this.coordinates = coordinates; this.trafficTower = null; } register(trafficTower) { this.trafficTower = trafficTower; } requestCoordinates() { if (this.trafficTower) return this.trafficTower.requestCoordinates(this); return null; } } // usage const tower = new TrafficTower(); const airplanes = [new Airplane(10), new Airplane(20), new Airplane(30)]; airplanes.forEach(airplane => { tower.register(airplane); }); console.log(airplanes.map(airplane => airplane.requestCoordinates())) // [[20, 30], [10, 30], [10, 20]]
- Observer Pattern
더보기하나의 객체가 상태를 변경하면 (게시자)
다른 모든 종속 객체가 자동으로 업데이트되는 (구독자)
객체 간의 일 대 다 종속성을 정의하는 패턴입니다.
addEventListener 또는 jQuery를 사용한 적이 있다면 익숙 할 것입니다.
예를 보면, Subject 클래스의 fire 메서드에서 Observer 클래스의 Update 메서드를 호출하여 state 값을 변경하고 있습니다.
class Subject { constructor() { this._observers = []; } subscribe(observer) { this._observers.push(observer); } unsubscribe(observer) { this._observers = this._observers.filter(obs => observer !== obs); } fire(change) { this._observers.forEach(observer => { observer.update(change); }); } } class Observer { constructor(state) { this.state = state; this.initialState = state; } update(change) { let state = this.state; switch (change) { case 'INC': this.state = ++state; break; case 'DEC': this.state = --state; break; default: this.state = this.initialState; } } } // usage const sub = new Subject(); const obs1 = new Observer(1); const obs2 = new Observer(19); sub.subscribe(obs1); sub.subscribe(obs2); sub.fire('INC'); console.log(obs1.state); // 2 console.log(obs2.state); // 20
- State Pattern
더보기객체가 내부 state 변화에 따라 행동을 변경할 수 있도록 하는 패턴입니다.
예를 보면, TrafficLight의 change 메서드가 current를 변경합니다.
그래서 change 메서드를 호출하면 sign 메서드로 호출되는 클래스가 달라집니다.
class TrafficLight { constructor() { this.states = [new GreenLight(), new RedLight(), new YellowLight()]; this.current = this.states[0]; } change() { const totalStates = this.states.length; let currentIndex = this.states.findIndex(light => light === this.current); if (currentIndex + 1 < totalStates) this.current = this.states[currentIndex + 1]; else this.current = this.states[0]; } sign() { return this.current.sign(); } } class Light { constructor(light) { this.light = light; } } class RedLight extends Light { constructor() { super('red'); } sign() { return 'STOP'; } } class YellowLight extends Light { constructor() { super('yellow'); } sign() { return 'STEADY'; } } class GreenLight extends Light { constructor() { super('green'); } sign() { return 'GO'; } } // usage const trafficLight = new TrafficLight(); console.log(trafficLight.sign()); // 'GO' trafficLight.change(); console.log(trafficLight.sign()); // 'STOP' trafficLight.change(); console.log(trafficLight.sign()); // 'STEADY' trafficLight.change(); console.log(trafficLight.sign()); // 'GO' trafficLight.change(); console.log(trafficLight.sign()); // 'STOP'
- Strategy Pattern
더보기특정 작업에 대한 대체 알고리즘의 캡슐화를 허용하는 패턴입니다.
알고리즘을 정의하고 클라이언트의 간섭이나 지식없이 런타임에서 상호교환 할 수 있도록 캡슐화합니다.
예를 보면, Commute 클래스가 모든 strategy 클래스들을 감쌉니다.
그리고 strategy 이름을 Bus, Taxi, PersonalCar로 지었습니다.
// encapsulation class Commute { travel(transport) { return transport.travelTime(); } } class Vehicle { travelTime() { return this._timeTaken; } } // strategy 1 class Bus extends Vehicle { constructor() { super(); this._timeTaken = 10; } } // strategy 2 class Taxi extends Vehicle { constructor() { super(); this._timeTaken = 5; } } // strategy 3 class PersonalCar extends Vehicle { constructor() { super(); this._timeTaken = 3; } } // usage const commute = new Commute(); console.log(commute.travel(new Taxi())); // 5 console.log(commute.travel(new Bus())); // 10
- Template Pattern
더보기알고리즘의 뼈대 정의 또는 작업 구현을 기반으로 하는 패턴입니다.
class Employee { constructor(name, salary) { this._name = name; this._salary = salary; } work() { return `${this._name} handles ${this.responsibilities() /* gap to be filled by subclass */}`; } getPaid() { return `${this._name} got paid ${this._salary}`; } } class Developer extends Employee { constructor(name, salary) { super(name, salary); } // details handled by subclass responsibilities() { return 'application development'; } } class Tester extends Employee { constructor(name, salary) { super(name, salary); } // details handled by subclass responsibilities() { return 'testing'; } } // usage const dev = new Developer('Nathan', 100000); console.log(dev.getPaid()); // 'Nathan got paid 100000' console.log(dev.work()); // 'Nathan handles application development' const tester = new Tester('Brian', 90000); console.log(tester.getPaid()); // 'Brian got paid 90000' console.log(tester.work()); // 'Brian handles testing'
Cheat Sheet
출처 www.celinio.net/techblog/?p=65
Useful posters of the GoF patterns – Celinio's technical blog : learning and adapting
www.celinio.net
'Today I Learned' 카테고리의 다른 글
TIL_200930(webpack) (0) 2020.10.01 TIL_200929 (REST API) (0) 2020.09.30 TIL_200920(아키텍쳐 패턴) (0) 2020.09.21 TIL_200919(GoF pattern) (0) 2020.09.18 TIL_200916([JS33] 4. 암시적 변환) (0) 2020.09.17