GOF 대표적인 10 가지 Design patterns

제이밍·2022년 4월 14일
9
post-thumbnail
post-custom-banner

What is design pattern ❓❗️

코드에서 반복되는 디자인 문제를 해결하기 위해 사용자 지정할 수 있는 패턴을 미리 정해둔 것입니다.

실제로 복사하여 붙여넣을 수 있는 알고리즘과 다르게 디자인 패턴은 프로그램에 복사 할 수 없기 때문에
자신의 프로그램 현실에 맞는 솔루션을 구현할 필요가 있습니다.

패턴의 역사 Gang of Four 👬👬

c++로 주로 개발하던 4명 프로그래머는 계속해서 직면하는 프로그래밍적 반복적인 문제를 해결하기 위해 다른 접근 방식을 갖는것의 생성 패턴, 객체 생성의 구조적 패턴, 서로 관련되어있는 패턴등에 따라 디자인 패턴을 분류하기 시작하였습니다.

이를 통해 객체 지향 설계의 다양한 문제를 해결하는 23개의 패턴을 선보였으며 이는 프로그래밍 분야에서 매우 인기를 얻기 시작했고, 네 명의 저자의 긴 이름으로 인해 우리는 이 책을 4인 1조의 책 이라고 부르기 시작해
GOF책 으로 축약 되었습니다.

The Catalog of Design Patterns

Creational patterns

  • 기존 코드의 유연성과 재사용을 증가시키는 다양한 객체 생성 메커니즘을 제공

Structural Patterns

  • 구조를 유연하고 효율적으로 유지하면서 개체와 클래스를 더 큰 구조로 어셈블하는 방법을 설명

Behavioral Patterns

  • 알고리즘 및 객체 간의 책임 할당과 관련

Creational patterns 🧤

SINGLETON

싱글톤은 클래스가 하나의 인스턴스만 갖도록 하는 동시에 이 인스턴스에 대한 전역 액세스 지점을 제공하는 생성 디자인 패턴입니다.

typescript example

class Settings{
  static instance: Settings;
	public readonly mode = 'dark';

  private constructor(){
	// 생성자를 비공개로 만들어 인스턴스화 할 수 없도록!
  }
  
  static getInstance(): Settings{
    //인스턴스가 이미 생성되었는지 확인하고 없는 경우에만 새로 생성한다 
    if(!Settings.instance){
      Settings.instance = new Settings();
    }
    return Settings.instance;
  }
}

const settings = Settings.getInstance

자바스크립트는 객체 특성상 단순히 전역 객체를 생성함으로써 위 패턴과 동일한 모든 기능을 구현할 수 있다.

const settings = {
  dark: 'true'
}

PROTOTYPE

코드를 클래스에 종속시키지 않고 기존 개체를 복사할 수 있는 생성 디자인 패턴입니다.

class Animal{}
// class inheritance
class Dog extends Animal {}
class Cat extends Animal {}

이러한 상속을 통한 프로그래밍의 가장 큰 문제점은 복잡도가 높아질 수 있다는 것이다.

하지만 이 문제를 해결할 수 있는 대체제로 프로토타입 패턴이 있습니다.
프로토타입 형태의 프로그래밍은 상속보다 훨씬 단순하기 표현 될 수 있습니다.

특히, 프로토타입언어라 불리는 자바스크립트 에서는 더욱이 그렇습니다.

typescript example

const zombie = {
  eatBrains(){
    	return 'yum 🧠`
  }
}

const chad = Object.create(zombie, {name: {value: 'chad'}});

console.log(chad) // {name: 'chad'}a

chad.eatBrains(); // 'yum 🧠'
// console에는 나오지 않던 메소드가 출력된다. 
// 이는 자바스크립트의 프로토타입 체인이 있기 때문 가능한 일

// 아래처럼 proto를 가져오면 eatBrains라는 메소드를 가져올 수 있지만 
// 정석인 방법은 getPrototypeOf 메소드를 사용하는 것!

chad.__proto__; // {eatBrains: f}
Object.getPrototypeOf(chad)


// 프로토타입 확장하기
Array.prototype.bad = function(){
  console.log('im bad')
}

[].bad() // im bad

BUILDER

복잡한 객체를 단계별로 구성할 수 있는 창작 디자인 패턴으로, 패턴을 사용하면 동일한 구성 코드를 사용하여 객체의 다양한 유형과 표현을 생성할 수 있습니다.

typescript example

class HotDog{
  constructor(
  	public bun: string,
  	public ketchup: boolean,
 	public mustard: boolean,
  	public kraut: boolean
  ){}
}

addKetchup(){
  this.ketchup = true;
  // this 를 리턴해줌으로써 메소드체이닝이 가능해진다.
  return this;
}
addMustard(){
  this.mustard = true;
  return this;
}
addKraut(){
  this.kraut = true;
  return this;
}

const myLunch = new HotDog('wheat', false, true, true)

// method chaining
myLunch
  		.addKetchup()
		.addMustard()
		.addKraut();

FACTORY

슈퍼클래스에서 객체를 생성하기 위한 인터페이스를 제공하지만 서브클래스가 생성될 객체의 유형을 변경할 수 있도록 하는 생성 디자인 패턴입니다.

typescript example

class IOSButton { }
class AndroidButton { }

const button1 = os === 'ios' ? new IOSButton() : new AndroidButton();
const button2 = os === 'ios' ? new IOSButton() : new AndroidButton();
  

class ButtonFactory{
  createButton(os:string):IOSButton | AndroidButton {
    if(os === 'ios'){
      return new IOSButton();
    } else{
      return new AndroidButton();
    }
  }
}

const factory = new ButtonFactory();
// smart object creation
const btn1 = factory.createButton(os);
const btn2 = factory.createButton(os);

Structural Patterns

FACADE

라이브러리, 프레임워크 또는 기타 복잡한 클래스 집합에 대한 단순화된 인터페이스를 제공하는 구조적 디자인 패턴, facade 패턴의 대표적인 예로 제이쿼리를 들 수 있다.

typescript example

class PlumbingSystem{
  setPressure(v: number){}
  turnOn(){}
  turnOff(){}
}

class ElectricalSystem{
  setVoltage(v: number){}
  turnOn(){}
  turnOff(){}
}

class House{
  private plumbing = new PlumbingSystem();
  private electrical = new ElectricalSystem();
  
  public turnOnSystems(){
    this.electrical.setVoltage(120);
    this.electrical,turnOn();
    this.plumbing.setPressure(500);
    this.plumbing.turnOn();
  }
  
  public shutDown(){
    this.plumbing.turnOff();
    this.electrical.turnOff();
  }
  
  // ugly details hidden
  const client = new House();
	client.turnOnSystems();
	client.shutDown();
}

PROXY

다른 개체에 대한 대체 또는 자리 표시자를 제공할 수 있는 구조적 디자인 패턴입니다. 프록시는 원래 개체에 대한 액세스를 제어하므로 요청이 원래 개체에 전달되기 전이나 후에 수행할 수 있습니다.

typescript example

const original = {name: 'jeff'}

const reactive = new Proxy(original, {
  	get(target, key){
      console.log('Tracking:', key);
      return target[key];
},
  set(target, key, value){
    console.log('updating UI...');
    return Reflect.set(target, key, value);
  },
});

reactive.name; //logs 'tracking name'
reactive.name = 'bob' //logs 'updating UI...'

Behavioral Patterns

ITERATOR

기본 표현(목록, 스택, 트리 등)을 노출하지 않고 컬렉션의 요소를 순회할 수 있는 동작 디자인 패턴입니다.

typescript example

const card = ['apple', 'banana', 'orange'];
for (const item of cart){
  console.log(item)
}

function range(start:number, end:number, step=1){
  return{
    [Symbol.iterator](){
      return this;
    },
    next(){
      if(start<end){
        start = start+step;
        return {value: start, done: false};
      }
      return {done: true, value: end};
    }
  }
}

for(const n of range(0,100,5)){
  console.log(n);
}

OBSERVER

관찰하는 개체에 발생하는 모든 이벤트에 대해 여러 개체에 알리는 구독 메커니즘을 정의할 수 있는 동작 디자인 패턴입니다. (one to many relationship)

typescript example

import {Subject} from 'rxjs'

const news = new Subject();

const tv1 = news.subscribe(v => console.log(v + 'via Den TV'));
const tv2 = news.subscribe(v => console.log(v + 'via Batcave TV'));
const tv3 = news.subscribe(v => console.log(v + 'via Airport TV'));

news.next('Breaking news: ');
news.next('The war is over');

MEDIATOR

개체 간의 혼란스러운 종속성을 줄일 수 있는 행동 디자인 패턴입니다. 패턴은 개체 간의 직접 통신을 제한하고 중재자 개체를 통해서만 협력하도록 합니다.

typescript example

class Airplain{
  land(){}
}
class Runway {
  clear: boolean;
}
class Tower {
  clearForLanding(runway: Runway, plain: Airplane){
    if(runway.clear){
      console.log(`Plane ${plane} is clear for landing`);
    }
  }
}
const runway25A = new Runway();
const runway25B = new Runway();
const runway7 = new Runway();

const a = new Airplane();
const b = new Airplane();
const c = new Airplane();

const tower = new Tower();
tower.clearForLanding(runway25A, a) 

Mediator의 대표적예로 express middleware가 있습니다.

import express from 'express'
const app = express();

function logger(req, res, next){
  console.log('Request Type:', req.method)
  next()
}

app.use(logger);

app.get('/', (req, res)=>{
  res.send('Hello World');
});

STATE

내부 상태가 변경될 때 객체가 동작을 변경할 수 있도록 하는 동작 디자인 패턴입니다.
객체가 클래스를 변경한 것처럼 보입니다.

typescript example

// switch hell
class Human{
  think(mood){
    switch(mood){
      case 'happy':
        return 'I am happy';
      case 'sad' :
        return 'I am sad';
      default:
        return 'I am neutral';
    }
  }
// Same method, different outcome
class HappyState implements State{
  think(){
    return 'I am happy';
  }
}
class SadState implements State{
  think(){
    return 'I am sad';
  }
}

state 패턴

class Human{
  state: State;
  constructor(){
    this.state = new happyState();
  }
 
  think(){
    return this.state.think();
  }

  changeState(state){
	this.state = state;
  }
}

Reference

https://www.youtube.com/watch?v=tv-_1er1mWI
https://refactoring.guru/design-patterns/catalog

profile
모르는것은 그때그때 기록하기
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 4월 14일

글 잘 읽었습니다~! 정리도 잘하시고 예제 코드들도 좋네요~!

1개의 답글