디자인 패턴 6. Command Pattern, 커맨드 패턴

Eddy·2023년 4월 14일
0

Javascript

목록 보기
19/27

디자인 패턴

커맨드 패턴이란?

요구사항을 객체로 캡슐화할 수 있으며,매개변수를 사용하여 여러 가지 다른 요구 사항을 집어넣을 수 있다.
요청 내역을 큐에 저장하거나 로그로 기록할 수 있고, 작업취소 기능도 지원한다.

  • 실행될 기능을 캡슐화함으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴
    • 즉, 이벤트가 발생했을 때 실행될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용하고자 할 때 유용하다.
  • 실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자(Invoker) 클래스와 실제 기능을 실행하는 수신자(Receiver) 클래스 사이의 의존성을 제거한다.
    • 따라서 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용 할 수 있도록 해준다.

구성요소

  • Client : ConcreteCommand를 생성하고 Reciver를 설정한다.

  • Invoker : 명령이 들어있고, exceute() 메서드를 호출함으로써 커맨드 객체에게 특정한 작업을 수행해 달라고 한다.

  • Command :
    모든 커맨드 객체에서 구현해야 하는 인터페이스이다.
    실행될 기능을 execute 메서드로 선언함

  • Reciver :
    요구 사항을 수행하기 위해서 어떤 일을 처리해야 하는지 알고 있는 객체.
    ConcreteCommand에서 execute 메서드를 구현할 때 필요한 클래스
    즉, ConcreteCommand의 기능을 실행하기 위해 사용하는 수신자 클래스

  • ConcreteCommand : 특정 행동과 리시버 사이를 연결해준다.

  • execute() : 메서드에는 리시버에 있는 메서드를 호출하여 요청된 작업을 수행한다.


예제

예제1 TypeScript

//Interface
abstract class ICommand {
    abstract execute(args): void;
}

//Receiver
class State {
	private _state: number;
	constructor(state) {
    	this._state = state;	
    }

	getState() {
    	return this._state;
    }
	setState() {
    	this._state = value;
    }

}

//Invorker
class BankManager () {
	private _state;
  	private _commands = {};
  	constructor() {
    	this._state = state;
    }
  	
  	registerCommands(...args) {
    	for (const cmd of args) {
        	this._commands[cmd.constructor.name] = cmd;
        }
    }
  	
  	executeCmd(cmdName, param) {
    	this._commands[cmdName].execute(this._state,param)
    }
}

//Command
class Deposit implements ICommand {
	execute(...params):void {
    	const [state, amount] = params;
      	const prevState = state.getState()
        state.setState(prevState + amount)
    }
}
class Withdrawal implements ICommand {
	execute(...params):void {
    	const [state, amount] = params;
      	const prevState = state.getState()
        state.setState(prevState - amount)
    }
}

//1.command 생성
const deposit = new Deposit();
const withdrawal = new Withdrawal();
//2.receiver 생성
const state = new State(0);
//3.invoker 인스턴스 & receiver 등록
const bankManager = new BankManager();
//4.command 등록
bankManager.registerCommands(deposit,withdrawal)
//5.command 지시
bankManager.executeCmd('Deposit',100)
bankManager.executeCmd('Deposit',1100)
console.log(state.getState());  // 1100
bankManager.executeCmd('withdrawal',1000)
console.log(state.getState());  // 100

예제 2

class Command {
	execute() {}
}

class PrintCommand extends Command {
	constructor(printStr) {
    	this.printStr = printStr;
    }
  
  	execute() {
    	console.log(`from print command : ${this.printStr}`)	
    }
}

const firstPrint = new PrintCommand('1')
const secondPrint = new PrintCommand('2')

firstPrint.execute()  // from print command : 1
secondPrint.execute() // from print command : 2

class Dog {
	sit() {
    	console.log('the dog sat down')
    }
  	stay() {
    	console.log('the dog is staying')
    }
}

class DogCommand extends Command {
	constructor (dog,commands) {
    	this.dog = dog;
      	this.commands = commands;
    }
  
  	execute() {
    	for (const command in this.commands) {
        	if (command == 'sit ') this.dog.sit()
          	if (command == 'stay ') this.dog.stay()
        }
    }
}

const baduk = new Dog();
const dogCommand = new DogCommand(baduk,['sit','stay','sit'])
dogCommand.execute() 
// 'the dog sat down'
// 'the dog is staying'
// 'the dog sat down'


class Invoker {
	constructor() {
    	this.commandList = [];
    }
  	
  	addCommand(command) {
    	this.commandList.push(command)
    }
  
  	runCommand() {
    	for (const command in this.commandList) {
        	command.execute();
        }
    }
}

const invoker = Invoker();
invoker.addCommand(firstPrint)
invoker.addCommand(secondPrint)
invoker.addCommand(dogCommand)

invoker.runCommand();
// from print command : 1
// from print command : 2
// 'the dog sat down'
// 'the dog is staying'
// 'the dog sat down'

예제 3 취소기능

class Calculator {
	constructor() {
    	this.value = 0;
      	this.history = [];
    }

  	executeCommand(command) {
    	this.value = command.execute(this.value)
      	this.history.push(command)
    }
  
  	undo() {
    	const command = this.history.pop()
        this.value = command.undo(this.value)
    }
}

class AddCommand {
	constructor(valueToAdd) {
    	this.valueToAdd = valueToAdd
    }
  
    execute(currentValue) {
      return currentValue + this.valueToAdd
    }

    undo(currentValue) {
      return currentValue - this.valueToAdd
    }
}

class MultiplyCommand {
	constructor(valueToMultiply) {
    	this.valueToMultiply = valueToMultiply
    }
  
    execute(currentValue) {
      return valueToMultiply * this.valueToMultiply
    }

    undo(currentValue) {
      return valueToMultiply / this.valueToMultiply
    }
}

const calculator = new Calculator();
calculator.executeCommand(new AddCommand(10)) 
calculator.executeCommand(new MultiplyCommand(2)) 
console.log(calculator.value) // 20 
calculator.undo() 
console.log(calculator.value) // 10 


class AddThenMultiplyCommand {
	constructor(valueToAdd,valueToMultiply) {
      this.addCommand = new AddCommand(valueToAdd)
      this.multiplyCommand = new MultiplyCommand(valueToMultiply)
    }
  
  	execute(currentValue) {
      	const newValue = this.addCommand.excute(currentValue)
    	retrun this.multiplyCommand.execute(newValue)
    }
  
  	undo(currentValue) {
          	const newValue = this.multiplyCommand.undo(currentValue)
    	retrun this.addCommand.undo(newValue)
    }
}

calculator.executeCommand(new AddThenMultiplyCommand(10,2)) 
console.log(calculator.value) // 20 
calculator.undo()
console.log(calculator.value) // 0

0개의 댓글