Skip to content

Latest commit

 

History

History
317 lines (212 loc) · 11 KB

File metadata and controls

317 lines (212 loc) · 11 KB

자바스크립트 디자인 패턴

생성자 패턴

생성자는 객체가 새로 만들어진 뒤 초기화하는 데에 사용되는 특별한 메서드

객체 생성

const newObject = {};
const newObject = new Object();

자바스크립트 처음 배울 때 {} 이것보다 new Object()를 썼던 것 같아요. new Object()를 쓰면 이것이 객체라도 더 명확하게 말해줄 수 있지 않나? 싶었던...

모듈 패턴

서로 다른 모듈 간의 클래스 또는 함수명 충돌을 방지할 수 있다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const newObject = {};
    </script>
    <script>
      const newObject = new Object();
    </script>
  </body>
</html>

newObject 변수가 충돌됨.

싱글톤 패턴

ex. 한 나라에는 한 번에 하나의 대통령만 존재할 수 있다. 업무를 할 때마다 동일한 대통령이 행동에 나서야 한다. 여기에서 대통령은 싱글톤이다.

클래스의 인스턴스가 오직 하나만 존재하도록 제한하는 패턴

class President {
  private static instance: President;

  private constructor() {
    // 생성자 숨기기
  }

  public static getInstance(): President {
    if (!President.instance) {
      President.instance = new President();
    }

    return President.instance;
  }

  private clone(): void {
    // 복제 비활성화
  }

  private wakeup(): void {
    // 데이터 역직렬화(unserialize) 비활성화
  }
}

const president1: President = President.getInstance();
const president2: President = President.getInstance();

console.log(president1 === president2); // true

필요할 때까지는 리소스나 메모리를 소모하지 않도록 지연 생성할 수 있다.

책에서는 싱글톤을 사용하지 말라고 하는 것 같네요.

종종 authorized axios instance, unauthorized axios instance 등등 싱글톤으로 몇 가지 인스턴스를 만들고 주구장창 가져다 쓰곤 했는데요, 이 방식에 대해 어떻게 생각하시나요? 그냥 axios 쓰는 것과, createInstance로 인스턴스를 만들어서 그걸 가져다 쓰는 방식 중에서 다들 어떻게 하고 계신지..!

프로토타입 패턴

ex. 복제된 양! 돌리(dolly)

생성할 객체의 유형이 원형 인스턴스에 의해 결정되며, 이를 복제하여 새로운 객체를 생성한다.

class Sheep {
  protected name: string;
  protected category: string;

  constructor(name: string, category: string = 'Mountain Sheep') {
    this.name = name;
    this.category = category;
  }

  setName(name: string): void {
    this.name = name;
  }

  getName(): string {
    return this.name;
  }

  setCategory(category: string): void {
    this.category = category;
  }

  getCategory(): string {
    return this.category;
  }

const original: Sheep = new Sheep('Jolly');
console.log(original.getName()); // Jolly
console.log(original.getCategory()); // Mountain Sheep

const cloned: Sheep = Object.create(original);
cloned.setName('Dolly');
console.log(cloned.getName()); // Dolly
console.log(cloned.getCategory()); // Mountain Sheep

프로토타입 패턴은 상속을 구현하는 쉬운 방법일 뿐만 아니라 성능에서의 이점도 챙길 수 있다. 객체 내의 함수를 정의할 때 복사본이 아닌 참조로 생성되어 모든 자식 객체가 동일한 함수를 가리키게 할 수 있기 때문이다.

기존 객체와 유사한 객체가 필요하거나 복제에 비해 생성 비용이 많이 드는 경우에 사용한다.

팩토리 패턴

ex. 인사 담당자의 경우, 각 포지션에 대해 한 사람이 모든 면접을 진행하는 것은 불가능하다. 채용 공고에 따라 면접 절차를 결정하고 다른 사람들에게 위임해야 한다.

abstract class HiringManager {
  // Factory method
  protected abstract makeInterviewer(): Interviewer;

  public takeInterview() {
    const interviewer = this.makeInterviewer();
    interviewer.askQuestions();
  }
}

interface Interviewer {
  askQuestions(): void;
}

class Developer implements Interviewer {
  askQuestions() {
    console.log('Asking about design patterns!');
  }
}

class CommunityExecutive implements Interviewer {
  askQuestions() {
    console.log('Asking about community building');
  }
}

class DevelopmentManager extends HiringManager {
  protected makeInterviewer(): Interviewer {
    return new Developer();
  }
}

class MarketingManager extends HiringManager {
  protected makeInterviewer(): Interviewer {
    return new CommunityExecutive();
  }
}

const devManager = new DevelopmentManager();
devManager.takeInterview(); // 출력: Asking about design patterns!

const marketingManager = new MarketingManager();
marketingManager.takeInterview(); // 출력: Asking about community building.

클래스에 일부 일반적인 처리가 있지만 필수 자식 클래스가 런타임에 동적으로 결정될 때 유용하다. 다른 말로 하면, 클라이언트가 정확히 어떤 자식 클래스가 필요한지 모를 때 사용된다.

참고


구조 패턴

클래스와 객체의 구성을 다룬다. 상속의 개념을 통해, 인터페이스와 객체를 구성하여 새로운 기능을 추가한다.

퍼사드 패턴

facade, 실제 모습을 숨기고 꾸며낸 겉모습만 세상에 드러냄.

심층적인 복잡성은 숨기고, 사용하기에 편리한 높은 수준의 인터페이스만 제공한다.

알게 모르게 많은 것들을 퍼사드 패턴으로 작성하고, 사용도 하지 않았나 싶네요! 이 회사 들어올 때 todo list를 관리하는 class를 만들어보는 라이브 코딩을 했었는데 그게 생각이 나네요.

서브 클래싱

저 class에서 super가 뭔지 몰랐는데 이번에 알았음용...

클래스 본문 내에서 super의 참조는 실행 맥락이 인스턴스 생성인지 클래스 초기화인지에 따라 슈퍼클래스의 생성자 자체 이거나 생성자의 prototype이 될 수 있다.

실행 맥락이 인스턴스 생성이라면 슈퍼클래스의 생성자를 호출하고, 클래스 초기화라면 슈퍼클래스의 prototype을 참조할 것 같네요.

믹스인

새롭게 만들어지는 클래스는 부모 클래스로부터 메서드와 속성을 부여 받는다.

동적으로 클래스를 만들 수 있는게 신기했어요.

const Person = (SuperClass) =>
  class extends SuperClass {
    walk() {
      console.log("walk");
    }
  };

class Student {
  study() {
    console.log("study");
  }
}

class StudentPerson extends Person(Student) {}

const student = new StudentPerson();
student.walk(); // () => void;
student.study(); // any;

근데 동적으로 정의된 method에 대해 타입 추론이 안되는 것 같네요..?

믹스인은 함수의 중복을 줄이고 재사용성을 높인다.

리액트 개발팀은 컴포넌트의 유지보수와 재사용을 복잡하게 만든다는 이유로 믹스인을 반대했다.

  • 종속성이 명확하지 않음. 어떤 mixin에서 선언되었길래 사용할 수 있는건지 모름
  • 이름이 충돌할 수 있음. AMixin에서 handleChange, BMixin에서도 handleChange를 선언할 수도 있음. 마지막에 선언된 것을 사용하겠지만 명식적이지 않아 버그 발생 가능성이 높음.
  • 코드 복잡도가 증가함. 유지보수가 어려워짐

데코레이터

코드 재사용을 목표로 한다. 기존 클래스에 동적으로 기능을 추가하기 위해 사용하는 패턴.

데코레이터 자체는 클래스의 기본 기능에 필수적이지 않다. 필수적이었다면 부모 클래스에 이미 구현되었을 것.

데코레이터를 사용하면 기존 시스템의 내부 코드를 힘겹게 바꾸지 않아도 기능을 추가할 수 있다.

전 이 데코레이터 설명이 믹스인과 어떻게 다른지 사실 잘 감이 안 왔어요...

인터페이스

JavaScript에서 구현된 Interface 코드를 찾아봤읍니다. 무려 10년 전!

JavaScript에서 구현한 Interface

플라이웨이트 패턴

연관된 객체끼리 데이터를 공유하게 하면서 애플리케이션의 메모리를 최소화하는 패턴.

디자인패턴, Flyweight Pattern, 플라이 웨이트 패턴 이 영상의 예시가 조금 더 쉬워서 이해하기 수월했어요.

매번 데이터를 생성하기 보다는, 공유될 수 있고 중복되지 않아도 되는 것들은 다른 클래스나 객체로 분리하고 이를 사용해 메모리 사용을 최소화한다.

  • 중앙 집중식 이벤트 핸들링

여러 요소들에 하나하나 이벤트를 바인딩하기 보다는 최상위 컨테이너에 이벤트를 한 번만 바인딩한다.

한창 자바스크립트 공부할 때 이런 방식을 자주 접하곤 했는데 막상 실무에선 써보지 않았네요. 이런 방식으로 최적화해보신 분..!?

행위 패턴

객체 간의 의사소통을 다룬다.

관찰자 패턴

한 객체가 변경될 때 이를 구독하고 있는 다른 객체들에게 변경되었음을 알린다.

발행/구독 패턴

이벤트를 발생시키는 발행자와 그 이벤트를 구독하는 구독자 사이에 토픽/이벤트 채널이 있음.

발행자와 구독자를 각자 독립적으로 유지함. 그리고 느슨한 결합을 도모함.

중재자 패턴

시스템의 구성 요소들 간에 직접적인 관계가 너무 많다면, 중앙 통제 포인트를 두어 모든 구성 요소들이 이를 통해 간접적으로 소통하도록 할 때가 되었다.

const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error) => {
      ...
    },
  }),
});

요런 친구도 중재자 패턴이라고 할 수 있을까요? 하위 ErrorBoundary에서 걸러지지 않은 에러들은 이렇게 최상위의 query client에서 처리해주고 있는디.

이벤트 집한 패턴과 중재자 패턴 결합하기

세부 로직은 중재자한테 위임할건데, 그 방식은 이벤트 집한 패턴으로 하겠뜸! 정도로만 이해했읍니다...

커맨드 패턴

객체 내부의 핵심 API가 변경되면 이 API를 사용하는 모든 객체를 수정해야 한다.

커맨드 패턴에서는 execute와 같은 실행을 위한 동작을 포함하는 커맨드 객체와, execute 메서드에 의해 실행할 동작을 가지고 있는 객체로 구성된다.

근데 예시에서 CarManager의 메서드 이름만 변경되어도, 해당 메서드를 사용하고 있는 코드들도 수정해야 되지 않나요??