diff --git "a/\354\261\225\355\204\260_7/\354\232\260\354\260\275\354\231\204.md" "b/\354\261\225\355\204\260_7/\354\232\260\354\260\275\354\231\204.md" index 2befe77..52e42ab 100644 --- "a/\354\261\225\355\204\260_7/\354\232\260\354\260\275\354\231\204.md" +++ "b/\354\261\225\355\204\260_7/\354\232\260\354\260\275\354\231\204.md" @@ -1,139 +1,119 @@ # 7. 자바스크립트 디자인패턴 (1/3) - - ### 생성패턴 -* 생성자 패턴 -* 모듈 패턴 -* 노출 모듈 패턴 -* 싱글톤 패턴 -* 프로토타입 패턴 -* 팩토리 패턴 - - +- 생성자 패턴 +- 모듈 패턴 +- 노출 모듈 패턴 +- 싱글톤 패턴 +- 프로토타입 패턴 +- 팩토리 패턴 ### 7.2 생성자 패턴 - - #### 7.2.1 객체 생성 ```js -// <= es5 +// <= es5 -const newObject = {} +const newObject = {}; Object.defineProperty(newObject, "name", { - value: "Foo", - writable: true, - enumerable: true, - configurable: true -}) + value: "Foo", + writable: true, + enumerable: true, + configurable: true, +}); //이렇게도 할당 가능 -newObject.name = "Bar" -console.log(newObject) +newObject.name = "Bar"; +console.log(newObject); ``` - - #### 7.2.2 생성자의 기본 특징 es6에서 도입된 class를 통해 객체를 생성하고 초기화할 수 있다. ```js class Person { - constructor(name) { - this.name = name - } + constructor(name) { + this.name = name; + } } -const bun = new Person("FooBar") -console.log(bun) +const bun = new Person("FooBar"); +console.log(bun); ``` - - ### 7.3 모듈 패턴 모듈 패턴을 잘 알지 못하고 처음보았지만, es module을 native하게 지원하고 사용할 수 있는 환경에서 유용성이 큰가? 에 대해아직 물음표인 것 같다. es module은 그 자체로 모듈로서 동작하는데, 왜 모듈 래퍼를 하나 더 씌우는 건지 궁금하다. - - 모듈 그 자체로의 장점은 단일 인스턴스를 보장한다는 점인 것 같다. 객체를 만들려면 singleton으로 몸 비틀기를 해야하는 Java에 비해 단일 인스턴스가 필요한 상황이면 모듈을 이용하는 것으로 충분해 보인다. - - - - ### 7.3.4 WeakMap을 사용하는 최신 모듈 패턴 WeakMap을 통한 접근제한 필요할 때가 있을 것 같지만, class의 instance로 관리하는 것과 유사해보인다. 1. 먼저, 모듈 변수를 이용한 패턴을 살펴보면, 두 가지 문제점을 볼 수 있다. (WeakMap ❌) -* 모듈 내의 변수를 공유하게 된다. 즉, 인스턴스 간의 독립적인 상태를 갖지 못한다. -* 동일 모듈에서 모듈 변수들에게 자유롭게 접근이 가능하다. +- 모듈 내의 변수를 공유하게 된다. 즉, 인스턴스 간의 독립적인 상태를 갖지 못한다. +- 동일 모듈에서 모듈 변수들에게 자유롭게 접근이 가능하다. ```js let counter = 0; let action; class Countdown { - constructor(initialCounter, initialAction) { - counter = initialCounter; // 모듈 스코프의 counter 변경 - action = initialAction; // 모듈 스코프의 action 변경 - } + constructor(initialCounter, initialAction) { + counter = initialCounter; // 모듈 스코프의 counter 변경 + action = initialAction; // 모듈 스코프의 action 변경 + } - dec() { - if (counter < 1) return; + dec() { + if (counter < 1) return; - counter--; // 모듈 스코프의 counter 감소 + counter--; // 모듈 스코프의 counter 감소 - if (counter === 0) { - action(); // 모듈 스코프의 action 호출 - } + if (counter === 0) { + action(); // 모듈 스코프의 action 호출 } + } } ``` - - 2. 클래스 내의 멤버 변수로 선언할 경우. -* 자바스크립트는 비공개 필드를 선언할 수 없었다. +- 자바스크립트는 비공개 필드를 선언할 수 없었다. ```js class Countdown { - counter; - action; + counter; + action; - constructor(counter, action) { - this.counter = counter; - this.action = action; - } + constructor(counter, action) { + this.counter = counter; + this.action = action; + } - dec() { - if (this.counter < 1) return; + dec() { + if (this.counter < 1) return; - this.counter--; // 프라이빗 필드 값 감소 + this.counter--; // 프라이빗 필드 값 감소 - if (this.counter === 0) { - this.action(); // 프라이빗 필드로 접근 - } + if (this.counter === 0) { + this.action(); // 프라이빗 필드로 접근 } + } } -const c = new Countdown(2, () => console.log('DONE')); -console.log(c.counter); //2 +const c = new Countdown(2, () => console.log("DONE")); +console.log(c.counter); //1 c.counter = -1; console.log(c.counter); // -1 ``` - - es2019에 추가된 private class member(`#`)가 도입되었다. 사실 private class member는 트랜스파일링 시, weakMap으로 트랜스파일링 된다. ```js @@ -142,35 +122,26 @@ class A { } // 트랜스파일링 -var _privateFieldA = /*#__PURE__*/new WeakMap(); +var _privateFieldA = /*#__PURE__*/ new WeakMap(); class A { constructor() { _privateFieldA.set(this, { writable: true, - value: 1 + value: 1, }); } } - ``` - - - - #### 7.4 노출 모듈 패턴 export로 비공개/공개 요소를 지정하는 것보다 포인터 객체를 만드는 것이 어떤 이점이 있는지 잘 모르겠다.. - - #### 7.5 싱글톤 패턴 Java의 싱글톤 패턴을 자바스크립트에서 사용하면 문제가 있는지 의심해봐야한다. - - #### 7.7 팩토리 패턴 싱글 팩토리 패턴 @@ -188,35 +159,136 @@ function createVechicle(data) { } ``` - - 위의 싱글 팩토리 패턴은 srp, ocp 위반이다. 그래서 나온 것이 추상 팩토리 메서드 패턴이다. 잘 동의가 안 되는게, 추상 팩토리를 만든다고 하더라도 진입점에서는 결국 분기처리 해주어야 하는 것이 아닌가? 결국 if,else 분기문의 위치만 달라진다고 느껴진다. 그러면, 인터페이스 복잡도, 추상도만 올라가는 것 같은데.. 어떤 이점이 있는지 잘 모르겠다. 유용함을 깨달으면 수정해놓겠지. +### 7.8 구조 패턴 + +구조 패턴은 클래스와 객체를 체계적으로 구성하는 방법에 관한 것 +### 7.10 퍼사드 패턴 +퍼사드란, 내부의 복잡한 로직을 외부에 편리한 높은 수준의 인터페이스를 제공하는 패턴. +이름을 적절히 추상화해서 외부에 노출하는 것이 중요하는 것이 중요하다. +### 7.10 믹스인 패턴 +믹스인은 서브클래스가 쉽게 상속받아 기능을 재사용할 수 있도록 하는 클래스 +### 7.11 서브 클래싱 패턴 + +부모 클래스 생성자를 호출(super())하는 것 + +```js +class SuperHero extends Person { + constructor(firstName, lastName, powers) { + super(firstName, lastName); + this.powers = powers; + } +} +``` + +### 7.12 믹스인 + +자바스크립트에서 다중 상속을 지원하지 않기 때문에, mixin 패턴을 통해 상속을 흉내낼 수 있다. + +```js +function LoggingMixin(Base: T) { + return class extends Base { + log(message: string) { + console.log(`[LOG]: ${message}`); + } + }; +} +class Product { + name: string; + price: number; + constructor(name: string, price: number) { + this.name = name; + this.price = price; + } +} +// Mixins을 통해 기능 추가 +const LoggingProduct = LoggingMixin(Product) +const product1 = new LoggingProduct("Laptop", 1500) +product1.log("Hello") +``` +React의 HOC와도 비슷해보인다. +```js +import React from "react"; +function withLogging(WrappedComponent) { + return class extends React.Component { + log(message) { + console.log(`[LOG]: ${message}`); + } + render() { + // HOC가 props를 WrappedComponent에 전달 + return ; + } + }; +} +// 기본 컴포넌트 +function Product({ name, price, log }) { + return ( +
+

{name}

+

Price: {price}

+ +
+ ); +} +// HOC를 통해 기능 추가 +const LoggingProduct = withLogging(Product); +``` +HOC는 이러한 간단한 예시에서는 적절한 추상화를 제공하고, 문제를 우아하게 해결하는 것처럼 보인다. 하지만, 경험적으로 HOC가 많아질수록, 복잡성과 유지보수를 어렵게 만든다. +### 7.13 데코레이터 패턴 +객체에 동적으로 기능을 추가, 확장할 수 있는 디자인 패턴 +상속이 컴파일 타임에 확정되는 데 비해, 데코레이터 패턴은 동적으로 추가할 수 있다. +```js +class Coffee { + getCost() { + return 5; // 기본 커피 가격 + } + getDescription() { + return "Basic Coffee"; + } +} +class MilkDecorator { + constructor(coffee) { + this.coffee = coffee; + } + getCost() { + return this.coffee.getCost() + 2; // 우유 추가 가격 + } + getDescription() { + return this.coffee.getDescription() + ", Milk"; + } +} +//개발자의 인자부하를 올리지는 않을까? 어떤 클래스의 데코레이터인지 파악하기 어려울 것 같다는 생각도 든다 +const coffee = new Coffee(); +const milkCoffee = new MilkDecorator(coffee); +``` +### 7.14 의사 클래스 데코레이터 +`Interface.ensureImplements`까지 하는 것은 투머치.. 타입스크립트를 사용하자