ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바스크립트 디자인 패턴(1)
    카테고리 없음 2021. 9. 26. 10:33
    728x90


    전통적인 디자인 패턴을 자바스크립트로 작성하면서 문제를 해결하는 방식에 대해 알아보겠습니다.

     

    1. Factory 패턴
      - 공장에서 찍어내듯이 인스턴스 생성 과정을 추상화 하는 것
      - 왜? 개체 생성과 구현 분리함으로 유연성 및 유지보수성 증대 및 클래스를 비공개로 유지 
      - 클로져를 활용하여 Private 변수를 선언하는 방법으로 활용
      // new 연산자를 활용한 개체 생성
      const dinner = new Dinner("PIZZA");
      // Factory 패턴 => 유지보수성 🚀
      const makeDinner = (food, _ = typeCheck(food, "string")) => {
          if (food == "PIZZA") return new Pizza(food);
          else if (food == "CHICKEN") return new Chicken(food);
          else if (food == "SALAD") return new Salad(food);
          else if (food == "KEBAB") return new Kebab(food);
          else {
              throw new Error("We dont have recipes!!");
          }
      };

      // Knex 패키지 (https://github.com/knex/knex/blob/faacfd726ea4c6ff809758edf88c2c0154af7717/lib/knex.js#L12)
      // Factory 패턴을 통해 다양한 검사를 진행하고 필요한 개체 생성하여 리턴
      function Knex(config) {
          let Dialect;
          if ( arguments.length === 0 || (!config.client && !config.dialect)) {
              Dialect = Client;
          } else if ( typeof config.client === "function" && config.client.prototype instanceof Client ) {
              Dialect = config.client;
          } else {
              const clientName = config.client || config.dialect;
              if (!SUPPORTED_CLIENTS.includes(clientName)) {
                  throw new Error(
                      `knex: Unknown configuration option 'client' value ${clientName}. Note that it is case-sensitive, check documentation for supported values.`
                  );
              }
              const resolvedClientName = resolveClientNameWithAliases(clientName);
              Dialect = require(`./dialects/${resolvedClientName}/index.js`);
          }
      
          if (typeof config.connection === "string") {
              config = Object.assign({}, config, {
                  connection: parseConnection(config.connection).connection,
              });
          }
          const newKnex = makeKnex(new Dialect(config));
          if (config.userParams) {
              newKnex.userParams = config.userParams;
          }
          return newKnex;
      }
    2. Builder 패턴
      - Helper 함수(post, send 등)를 활용해서 인자들을 모으고 마지막에 인스턴스를 생성하는 패턴
      - 왜? 복잡한 개체 생성을 단순화
      // superagent 패키지 (https://www.npmjs.com/package/superagent)
      superagent
        .post('/api/pet')
        .send({ name: 'Manny', species: 'cat' }) // sends a JSON post body
        .set('X-API-Key', 'foobar')
        .set('accept', 'json')
        .end((err, res) => {
          // Calling the end function will send the request
        });
    3. Revealing Constructor 패턴
      - 객체가 생성된 순간에만 필요한 객체의 내부기능을 노출하도록 하는 패턴 (예시 Promise)
      - 왜? 강력한 캡슐화가 제공 가능
      - 전통적인 디자인패턴에 존재하지 않음 => JS 커뮤니티에서 새롭게 언급
      // resolve, reject의 실행자를 입력받아 Promise 내부를 초기화/변경을 진행
      // 초기화 및 변경 이후에는 Promise 흐름제어 불가
      new Promise((res, rej) => {
          //...
      });
    4. SingleTon 패턴
      - 클래스의 인스턴스 생성을 하나로 강요하여 접근을 중앙 집중화하도록 하는 패턴
      - 왜? 상태 정보 공유 및 리소스 최적화
      - JS 진영에서는 모듈시스템으로 의미없는 패턴 => 전체 경로를 키로 모듈캐싱이 진행되어 불러온 패키지가 싱글톤이라는 것을 보장
      - 문제점 👻 :  복잡한 상황에 맞추어 동작하는 시스템을 구현에 적합하지 않음

    5. Dependency Injection 패턴
      - 앞서의 복잡한 상황에서 싱글톤의 문제를 해결하는 방식으로 종속성을 주입하는 방식으로 해결 가능
      - 문제점은 사람이 종속성을 쉽게 파악하기 어려워 실제로는 프로그래밍이 더욱 어려워짐

    6. Proxy 패턴
      - 다른 개체에 대한 액세스를 제어하여 동작을 보안하거나 추가적인 제어를 실행하는 패턴
      - 왜?  데이터 보안 및 검증 등 추가적인 제어를 손쉽게 구성 가능
      - 컴포지션 및 팩토리 함수 / 몽키패치 / ES6 Proxy 개체를 기반으로 구현 가능
      class Stack {
          #store;
          constructor() {
              this.#store = [];
          }
          push(data) {
              this.#store.push(data);
          }
          pop() {
              this.#store.pop();
          }
          show() {
              return this.#store.join();
          }
      }
      // 컴포지션을 활용한 프록시 구현, 비슷한 방식으로 팩토리 함수를 활용해서도 구현가능 
      class SaftyStack {
          #stack;
          constructor(stack) {
              this.#stack = stack;
          }
          push(data) {
              this.#stack.push(data); // 단순 위임
          }
          pop() {
              if (this.#stack.show().length === 0) // 추가 제어 로직
                  throw Error("stack is empty");
              this.#stack.pop();
          }
          show() {
              this.#stack.show(); // 단순 위임
          }
      }​
      // ES6의 Proxy 개체를 활용한 방법
      const safeStackProxyHandler = {
          get: (target, property) => {
              if (property === "pop") {
                  return () => {
                      if (target.show().length === 0)
                          throw Error("stack is empty [PROXY]");
                      return target.pop();
                  };
              }
              return target[property];
          },
      };
      const _sfyStackWithProxy = new Proxy(_stack, safeStackProxyHandler);
      _sfyStackWithProxy.pop();
    7. Decorator 패턴
      - 기존 개체의 인스턴스에 새로운 동작을 추가하는 패턴(상속X)
      - 구현전략은 프록시 패턴과 동일하며 서로 상호 교환 가능

    8. Adaptor 패턴
      - 기존 개체의 인터페이스를 다른 형태로 변경하여 제공하는 패턴
      - 마찬가지로 구현전략은 프론시 및 데코레이터와 비슷
    9.  
    10.  
    11.  

    12.    

     

     

    2. 

     

     

Designed by Tistory.