JavaScript Design Patterns edward - Lee-hyuna/33-js-concepts-kr GitHub Wiki

JavaScript Design Patterns

Constructor Pattern μƒμ„±μž νŒ¨ν„΄

전톡적인 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ— μžˆμ–΄μ„œ μƒμ„±μž(constructor)λŠ” νŠΉλ³„ν•œ λ©”μ„œλ“œλ‘œ μƒˆλ‘­κ²Œ 생성할 객체의 μ΄ˆκΈ°ν™”μ— μ‚¬μš©λ˜κ³  ν•œλ²ˆ μƒμ„±λœ κ°μ²΄λŠ” λ©”λͺ¨λ¦¬μƒμ— μ €μž₯μ΄λ©λ‹ˆλ‹€. μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” 거의 λͺ¨λ“  것이 객체이기에 객체 μƒμ„±μžμ— λŒ€λΆ€λΆ„μ˜ 관심을 κ°€μ§€κ²Œ λ©λ‹ˆλ‹€.

객체 μƒμ„±μžλŠ” 객체λ₯Ό μ‚¬μš©ν•  μ€€λΉ„λ₯Όν•˜κ³  객체λ₯Ό 처음 생성 ν•  λ•Œ μƒμ„±μžμ—μ„œ 멀버 속성 및 λ©”μ„œλ“œ 값을 μ„€μ •ν•˜λŠ” 데 μ‚¬μš©ν•  μˆ˜μžˆλŠ” 인수λ₯Ό λ°›μ•„λ“€μ΄λŠ” νŠΉμ • μœ ν˜•μ˜ 객체λ₯Ό λ§Œλ“œλŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.

1. Object Creation

κ°€μž₯ 일반적인 객세 μƒμ„±ν•˜λŠ” 방법

2. Basic Constructors

function person(firstName,lastName){
  
  this.firstName = firstName;
  
  this.lastName = lastName;
  
  this.fullName = function(){
    return this.firstName + " " + this.lastName;
  }
  
}

var person1 = new person('Akash','Pal');
var person2 = new person('Black','Panther');
person1.fullName(); //"Akash Pal"
person2.fullName(); //"Black Panther"
person1 //{firstName: "Akash", lastName: "Pal", fullName: Ζ’}
person2 //{firstName: "Black", lastName: "Panther", fullName: Ζ’}

3. Constructor with prototypes.

Module Pattern λͺ¨λ“ˆ νŒ¨ν„΄

일반적으둜 λͺ¨λ“ˆ νŒ¨ν„΄μ€ μ›λž˜ 기쑴의 μ†Œν”„νŠΈμ›¨μ–΄ μ—”μ§€λ‹ˆμ–΄λ§μ—μ„œ ν΄λž˜μŠ€μ— λŒ€ν•œ private κ³Ό publicν•œ μΊ‘μŠν™”λ₯Ό μ œκ³΅ν•˜λŠ” λ°©λ²•μœΌλ‘œ μ •μ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ λͺ¨λ“ˆ νŒ¨ν„΄μ€ 단일 객체 내에 곡개/λΉ„κ³΅κ°œ λ©”μ†Œλ“œμ™€ λ³€μˆ˜λ₯Ό λͺ¨λ‘ 포함할 수 μžˆλ„λ‘ 클래슀 κ°œλ…μ„ λ”μš± λͺ¨λ°©ν•˜μ—¬ νŠΉμ • 뢀뢄을 κΈ€λ‘œλ²Œ λ²”μœ„μ—μ„œ μ°¨λ‹¨ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. 이 κ²°κ³Ό νŽ˜μ΄μ§€μ˜ μΆ”κ°€ μŠ€ν¬λ¦½νŠΈμ— μ •μ˜λœ λ‹€λ₯Έ ν•¨μˆ˜μ™€ μΆ©λŒν•  κ°€λŠ₯성이 κ°μ†Œν•©λ‹ˆλ‹€.

var personModule = (function(){
  
  var firstName;
  var lastName;
  
  return{
    setName(f,l){
      firstName = f;
      lastName = l;
    },
    getName(){
      return firstName + " " + lastName;
    }
  }
  
})();

personModule.setName('akash','pal')
personModule.getName() //"akash pal"

Revealing Module Pattern λͺ¨λ“ˆ λ…ΈμΆœ νŒ¨ν„΄

개인 λ²”μœ„μ—μ„œ λͺ¨λ“  ν•¨μˆ˜μ™€ λ³€μˆ˜λ₯Ό κ°„λ‹¨ν•˜κ²Œ μ •μ˜ν•˜κ³  κ³΅κ°œν•˜κ³ μžν•˜λŠ” 개인 κΈ°λŠ₯에 λŒ€ν•œ 포인터가 μžˆλŠ” 읡λͺ… 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” μ—…λ°μ΄νŠΈ 된 νŒ¨ν„΄μž…λ‹ˆλ‹€.

var personModule = (function(){
  
  var firstName;
  var lastName;
  
  function setName(f,l){
    firstName = f;
    lastName = l;
  }
  
  function getName(){
    return firstName + " " + lastName;
  }
  
  return{
    setName:setName,
    getName:getName
  }
  
})();

personModule.setName('akash','pal');
personModule.getName(); //"akash pal"

Singleton Pattern 싱글톀 νŒ¨ν„΄

싱글톀 νŒ¨ν„΄μ€ 클래슀의 μΈμŠ€ν„΄μŠ€ν™”λ₯Ό 단일 객체둜 μ œν•œν•˜κΈ° λ•Œλ¬Έμ— μ•Œλ €μ Έ μžˆμŠ΅λ‹ˆλ‹€. κ³ μ „μ μœΌλ‘œ 싱글톀 νŒ¨ν„΄μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 경우 클래슀의 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” λ©”μ†Œλ“œλ‘œ 클래슀λ₯Ό μž‘μ„±ν•˜μ—¬ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μΈμŠ€ν„΄μŠ€κ°€ 이미 μ‘΄μž¬ν•˜λŠ” 경우 ν•΄λ‹Ή 객체에 λŒ€ν•œ μ°Έμ‘°λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

var singleton = (function(){

  var instance;
  
  function init(){
    
    var name;
    
    this.setName = function(name){
       this.name = name;
    }
    
    this.getName = function(){
      return this.name;
    }
    
    return{
      setName:setName,
      getName:getName
    }
      
  }
  
  function getInstance(){
    
    if(!instance){
      instance = init();
    }
    
    return instance;
  }
    
  return{
    getInstance:getInstance
  }  
  
})();


var one = singleton.getInstance();
var two = singleton.getInstance();

//the two instance are same
one == two //true

one.setName('Akash');
two.getName(); //"Akash"

Observer Pattern μ˜΅μ Έλ²„ νŒ¨ν„΄

μ˜΅μ €λ²„λŠ” 객체(주체둜 μ•Œλ €μ Έ 있음)κ°€ 객체(κ΄€μ°°μž)에 따라 객체 λͺ©λ‘μ„ μœ μ§€ν•˜κ³  μƒνƒœ 변경을 μžλ™μœΌλ‘œ μ•Œλ €μ£ΌλŠ” λ””μžμΈ νŒ¨ν„΄μž…λ‹ˆλ‹€.

  • Subject: κ΄€μ°°μž λͺ©λ‘μ„ μœ μ§€ν•˜κ³  κ΄€μ°°μž μΆ”κ°€ λ˜λŠ” 제거λ₯Ό μš©μ΄ν•˜κ²Œ ν•©λ‹ˆλ‹€.

  • Observer: λŒ€μƒμ˜ μƒνƒœ 변경을 μ•Œλ¦΄ ν•„μš”κ°€ μžˆλŠ” 객체에 λŒ€ν•œ μ—…λ°μ΄νŠΈ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

  • ConcreteSubject: μƒνƒœ 변화에 λŒ€ν•œ κ΄€μ°°μžμ—κ²Œ μ•Œλ¦Όμ„ μ•Œλ¦¬κ³ , ConcreteObservers의 μƒνƒœλ₯Ό μ €μž₯ν•©λ‹ˆλ‹€.

  • ConcreteObserver: ConcreteSubject에 λŒ€ν•œ μ°Έμ‘°λ₯Ό μ €μž₯ν•˜κ³  μƒνƒœκ°€ μ£Όμ œμ™€ μΌμΉ˜ν•˜λ„λ‘ κ΄€μ°°μžμ— λŒ€ν•œ μ—…λ°μ΄νŠΈ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.

Publish/Subscribe Pattern

μ˜΅μ Έμ„œ νŒ¨ν„΄μ€ 주제 μ•Œλ¦Όμ„ μˆ˜μ‹ ν•˜λ €λŠ” κ΄€μ°°μž(λ˜λŠ” 객체)κ°€ 이벀트λ₯Ό λ°œμƒμ‹œν‚€λŠ” 객체(주제)에 관심을 κ΅¬λ…ν•΄μ•Όν•©λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ Publish/Subscribe νŒ¨ν„΄μ€ μ•Œλ¦Όμ„ μˆ˜μ‹ ν•˜λ €λŠ” 객체(κ΅¬λ…μž)와 이벀트λ₯Ό λ°œμƒμ‹œν‚€λŠ” 객체(κ²Œμ‹œμž) μ‚¬μ΄μ—μžˆλŠ” topic/event 채널을 μ‚¬μš©ν•©λ‹ˆλ‹€. 이 이벀트 μ‹œμŠ€ν…œμ„ 톡해 μ½”λ“œλŠ” κ°€μž…μžκ°€ ν•„μš”λ‘œν•˜λŠ” 값을 ν¬ν•¨ν•˜λŠ” μ‚¬μš©μž 지정 인수λ₯Ό 전달할 μˆ˜μžˆλŠ” μ‘μš© ν”„λ‘œκ·Έλž¨ νŠΉμ • 이벀트λ₯Ό μ •μ˜ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ μ•„μ΄λ””μ–΄λŠ” κ΅¬λ…μžμ™€ κ²Œμ‹œμž κ°„μ˜ 쒅속성을 ν”Όν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ΄λŠ” μ μ ˆν•œ 이벀트 처리기λ₯Ό κ΅¬ν˜„ν•˜λŠ” κ°€μž…μžλ₯Ό κ²Œμ‹œμžκ°€ λΈŒλ‘œλ“œ 캐슀트 ν•œ ν† ν”½ μ•Œλ¦Όμ„ λ“±λ‘ν•˜κ³  μˆ˜μ‹  ν•  μˆ˜μžˆκ²Œν•˜λ―€λ‘œ μ˜΅μ Έλ²„ νŒ¨ν„΄κ³Ό λ‹€λ¦…λ‹ˆλ‹€.

이미지 μ£Όμ†Œ : https://miro.medium.com/max/3028/1*-HzQOW37lYfr2HWQRPDHvw.png

Mediator Pattern

A Mediator is an object that coordinates interactions (logic and behavior) between multiple objects. It makes decisions on when to call which objects, based on the actions (or inaction) of other objects and input.

Prototype Pattern

ν”„λ‘œν† νƒ€μž… νŒ¨ν„΄μ€ ν”„λ‘œν† νƒ€μž… 상속을 κΈ°λ°˜μœΌλ‘œν•˜κ³  λ‹€λ₯Έ 객체의 ν”„λ‘œν† νƒ€μž…μœΌλ‘œ μž‘λ™ν•˜λŠ” 객체λ₯Ό μƒμ„±ν•˜λŠ” κ²ƒμœΌλ‘œ 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.
ν”„λ‘œν† νƒ€μž… 객체 μžμ²΄λŠ” μƒμ„±μžκ°€ μƒμ„±ν•˜λŠ” 각 객체의 blueprint(청사진)λ₯Ό 효과적으둜 μ‚¬μš©λ©λ‹ˆλ‹€. μ‚¬μš©λœ μƒμ„±μž ν•¨μˆ˜μ˜ ν”„λ‘œν† νƒ€μž…μ— 예λ₯Ό λ“€λ©΄ μ•„λž˜μ˜ μ½”λ“œ μƒ˜ν”Œμ— 따라 nameμ΄λΌλŠ” 속성이 포함 된 경우 λ™μΌν•œ μƒμ„±μžμ— μ˜ν•΄ 생성 된 각 객체에도 λ™μΌν•œ 속성이 μžˆμŠ΅λ‹ˆλ‹€.

ECMAScript 5 ν‘œμ€€μ— μ •μ˜ 된 μ‹€μ œ ν”„λ‘œν† νƒ€μž… μƒμ†μ—λŠ” Object.createλ₯Ό μ‚¬μš©ν•΄μ•Όν•©λ‹ˆλ‹€.

function person(firstName,lastName){
  
  this.firstName = firstName;
  
  this.lastName = lastName;
  
}

person.prototype.fullName = function(){
    return this.firstName + " " + this.lastName;
}

var person1 = new person('Akash','Pal');
var person2 = new person('Black','Panther');
person1 //{firstName: "Akash", lastName: "Pal"}
person2 //{firstName: "Black", lastName: "Panther"}
person1.fullName() //"Akash Pal"
person2.fullName() //"Black Panther"

Object.createλ₯Ό 직접 μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν”„λ‘œν† νƒ€μž… νŒ¨ν„΄

Command Pattern

Command νŒ¨ν„΄μ€ λ©”μ†Œλ“œ 호좜, μš”μ²­ λ˜λŠ” μ˜€νΌλ ˆμ΄μ…˜μ„ 단일 였브젝트둜 μΊ‘μŠν™”ν•˜λŠ” 데 λͺ©μ μ΄ 있으며 μž¬λŸ‰μ— 따라 μ‹€ν–‰ν•  μˆ˜μžˆλŠ” λ©”μ†Œλ“œ ν˜ΈμΆœμ„ 맀개 λ³€μˆ˜ν™”ν•˜κ³  전달할 수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ μ•‘μ…˜μ„ κ΅¬ν˜„ν•˜λŠ” κ°μ²΄μ—μ„œ μ•‘μ…˜μ„ ν˜ΈμΆœν•˜λŠ” 객체λ₯Ό λΆ„λ¦¬ν•˜μ—¬ ꡬ체적인 클래슀(객체)λ₯Ό ꡐ체 ν•  λ•Œ μ „λ°˜μ μΈ μœ μ—°μ„±μ„ 크게 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

var name = {
  fName:'aaa',
  lName:'bbb',
  setName:function(fName,lName){
    this.fName = fName;
    this.lName = lName;
  },
  getName:function(){
     return this.fName + " " + this.lName;
  }
}

name.execute = function(key){
   var methodName = name[key];
   var functionParamsArray = [].splice.call(arguments,1);
   return methodName.apply(name,functionParamsArray);   
}

name.execute('setName','Akash','Pal');
console.log(name.execute('getName'));//Akash Pal

Facade Pattern

FacadesλŠ” jQuery와 같은 JavaScript λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ μ’…μ’… λ³Ό μˆ˜μžˆλŠ” ꡬ쑰적 νŒ¨ν„΄μž…λ‹ˆλ‹€. κ΅¬ν˜„μ€ κ΄‘λ²”μœ„ν•œ λ™μž‘μ„ 가진 λ©”μ†Œλ“œλ₯Ό 지원할 수 μžˆμ§€λ§Œ μ΄λŸ¬ν•œ λ©”μ†Œλ“œμ˜ "facade(μ •λ©΄)" λ˜λŠ” μ œν•œλœ μΆ”μƒν™”λ§Œ μ‚¬μš©μ„ μœ„ν•΄ κ³΅κ°œλ©λ‹ˆλ‹€.

FacadesλŠ” λͺ¨λ“ˆ νŒ¨ν„΄κ³Ό 같은 λ‹€λ₯Έ νŒ¨ν„΄κ³Ό 톡합 될 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

Factory Pattern

νŒ©ν† λ¦¬ νŒ¨ν„΄μ€ 객체 생성 κ°œλ…κ³Ό κ΄€λ ¨λœ 또 λ‹€λ₯Έ 생성 νŒ¨ν„΄μž…λ‹ˆλ‹€. μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ νŒ¨ν„΄κ³Ό λ‹€λ₯Έ 점은 λͺ…μ‹œ 적으둜 μƒμ„±μžλ₯Ό μ‚¬μš©ν•  ν•„μš”κ°€ μ—†λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. λŒ€μ‹  νŒ©ν† λ¦¬λŠ” 객체λ₯Ό μƒμ„±ν•˜κΈ°μœ„ν•œ 일반 μΈν„°νŽ˜μ΄μŠ€λ₯Ό 제곡 ν•  수 있으며, μ—¬κΈ°μ„œ μƒμ„±ν•˜λ €λŠ” νŒ©ν† λ¦¬ 객체의 μœ ν˜•μ„ 지정할 수 μžˆμŠ΅λ‹ˆλ‹€.

function Bike(options){
  this.wheels = 2;
  this.color = options.color;
}

function Car(options){
  this.wheels = 4;
  this.color = options.color;
}

function VehicleFactory(){}

VehicleFactory.prototype.createVehicle = function(options){
		
    switch(options.type){
    	case 'Bike': 
          this.vehicleClass = Bike;
      break;
      case 'Car': 
          this.vehicleClass = Car;
      break;
      default: 
          this.vehicleClass = Bike;
    }
    
    return new this.vehicleClass(options);
}

var vehicleFactory = new VehicleFactory();

var bike = vehicleFactory.createVehicle({
	type:'Bike',
  color:'black'
});

console.log(bike); //Bike {wheels: 2, color: "black"}

var car = vehicleFactory.createVehicle({
	type:'Car',
  color:'white'
});

console.log(car); //Car {wheels: 4, color: "white"}

이미지 μ£Όμ†Œ : https://miro.medium.com/max/2976/1*vS04c6QeLjIL_oj_pc0CHA.png

Mixin Pattern

Mixins은 κΈ°λŠ₯ μž¬μ‚¬μš©μ„ μœ„ν•΄ μ„œλΈŒ 클래슀 λ˜λŠ” μ„œλΈŒ 클래슀 그룹에 μ˜ν•΄ μ‰½κ²Œ 상속 될 μˆ˜μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” ν΄λž˜μŠ€μž…λ‹ˆλ‹€.

function Person(firstName,lastName){
  this.firstName = firstName; 
  this.lastName = lastName;
}

Person.prototype.fullName = function(){
    return this.firstName + " " + this.lastName;
}

function Superhero(firstName,lastName,powers){
  //super call
	Person.call(this,firstName,lastName);
  this.powers = powers;
}

Superhero.prototype = new Object(Person.prototype);

Superhero.prototype.showPowers = function(){
		return this.powers;
}

var superhero1 = new Superhero('Iron','Man',['Flying suit','Jarvis']);
console.log(superhero1.fullName() + '-' + superhero1.showPowers()); //Iron Man-Flying suit,Jarvis

var superhero2 = new Superhero('Captain','America',['strength','endurance','healing']);
console.log(superhero2.fullName() + '-' + superhero2.showPowers()); //Captain America-strength,endurance,healing