From f08105226377d57d5dec8138e3f891b381a92f19 Mon Sep 17 00:00:00 2001 From: Changwan <47740690+WooWan@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:34:48 +0900 Subject: [PATCH] =?UTF-8?q?[=EC=9A=B0=EC=B0=BD=EC=99=84]=20=EC=B1=95?= =?UTF-8?q?=ED=84=B0=207:=20=EC=9E=90=EB=B0=94=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EB=94=94=EC=9E=90=EC=9D=B8=20=ED=8C=A8=ED=84=B4=20?= =?UTF-8?q?(3/3)=20=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 7-3 * 오타 * chapter 8 --- .../\354\232\260\354\260\275\354\231\204.md" | 78 ++++++ .../\354\232\260\354\260\275\354\231\204.md" | 227 ++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 "\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" 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 52e42ab..a51b764 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" @@ -292,3 +292,81 @@ const milkCoffee = new MilkDecorator(coffee); ### 7.14 의사 클래스 데코레이터 `Interface.ensureImplements`까지 하는 것은 투머치.. 타입스크립트를 사용하자 + +### 7.16 플라이웨이트 패턴 + +플라이웨이트 패턴은 반복되고 비효율적으로 데이터를 공유하는 코드를 최적화하는 구조적 해결 방법 + +OOP + Data Oriented Programming 과 비슷한 느낌, 데이터 응집성에 포커스를 맞춘다. + +`checkoutMember`, `dueReturnDate` 는 Book 의 속성? -> 아니다. + +OOP 의 메모리 과도한 점유를 `상태`와 `데이터`의 결합도를 낮춘 형태. 결국 중요한 것은 응집도 + +### 7.17 행위 패턴 + +행위패턴이란 `객체간의 의사소통` + +- 관찰자 패턴 +- 중재자 패턴 +- 커맨드 패턴 + +### 7.18 관찰자 패턴 + +관찰자 패턴은 update를 기반으로 상태를 관리 (Subject, Observer) + +주체를 업데이트 할때, 데이터 변경을 감지할 때 `update`를 기반으로 동작한다. + +Subject: Observer: 1: N, (N:M이 될수도 있음) + +> Subject.action -> Subject.nofity(Observer.update()) 형태로 Observer에게 Subject의 상태 변경을 알린다. + +``` +Subject: 데이터의 주체 + +action: notify() 를 호출한다. +addObserver, removeObserver: Observer => void +notify: data => void // Observer.update() 자신을 구독하고 있는 Observer에게 상태변화를 알림. + + +Observer: Subject의 관찰자. +update: () => T // 상태변화에 따른 특정 액션 +``` + +### 7.18.1 관찰자 패턴과 발행/구독 패턴의 차이점 + +Pub/sub 패턴과의 차이는 변경 전파 책임 + +관찰자 패턴의 변경 감지 전파의 책임은 Subject에게 있다. + +시그니처를 보았을 때는 Pub/sub 더 유연해보이고, 상태 변화/감지의 책임이 더 잘 나누어진 것으로 보였음 + +하지만, 대부분의 경우는 Subject -> Observer로 바로 상태변화를 알리는 것이 명료해보이지만, 응집도는 많이 떨어짐 + +아래 사고를 거쳐서 계층의 필요타당성을 생각해보기 + +- 두 객체간의 결합도를 낮추는 것이 꼭 필요할 때 (분리가 필요한 계층, 계층적으로 달라야 필요성이 있을 듯) +- 발행/구독 계층이 어떤 문제를 해결해주는가? + +예시 중, 사용자와 리뷰의 관계가 결합도를 낮춰야할만큼의 다른 계층인가? -> 아니라고 생각함, 오히려 응집도가 낮아짐(161p) + +EventEmitter 도 떠오르는데, 어떤 Event가 emit되면 특정 함수를 호출하는 형태도 pub/sub의 일부와 비슷해보인다. + +RxJS 처음 본 인상은 Effect(state) => ui 형태로 사이드이펙트를 잘 제어할 수 있으면, 유지보수하기 좋은 형태를 만들 수 있을 것 같지만, + +사이드이펙트 관리에 대한 이해가 부족하면 오히려 독이 있을 것 같다 (debugging, 참조 투명성, descriptive name의 부재 등) + +### 7.19 중재자 패턴 + +중재자 패턴은 하나의 객체가 이벤트 발생 시, 다른 여러 객체들에게 알림을 보낼 수 있는 디자인 패턴 -> 관찰자 패턴가 뭐가 다르지? + +### 7.20 커맨드 패턴 + +명령을 trigger하는 객체와 명령을 execute 하는 객체가 분리되어 있는 패턴 + +책의 예시는 실질적으로 결합도를 낮추지 못한다. 불필요한 추상화 + +-> `buyVehicle` 이 `buyCar` 로 변경되면 execute('buyCar')도 함께 변경되어야 한다. + +결합도 관련해서 재밌게 읽은 글 남깁니다! +https://maxkim-j.github.io/posts/coupling diff --git "a/\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" "b/\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" new file mode 100644 index 0000000..70b3cb9 --- /dev/null +++ "b/\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" @@ -0,0 +1,227 @@ +# 자바스크립트 MV* 패턴 + + + +## 8.1 MVC 패턴 + +MVC 패턴에서 Model과 View가 Subject, Observer 관계로 결합도를 낮춘 형태 + +* Model + + ```js + // Model: 데이터 관리와 비즈니스 로직 + class TodoModel { + constructor() { + this.todos = []; + this.observers = []; + } + + addTodo = (text) => { + this.todos.push({ id: Date.now(), text, completed: false }); + this.notify(); + } + + addObserver = (observer) => { + this.observers.push(observer); + } + + notify = () => { + this.observers.forEach(observer => observer(this.todos)); + } + } + ``` + +* Controller + + Controller에서 View에 함수를 binding, model에 observer를 추가한다. + + ```js + // Controller: Model과 View 연결 + class TodoController { + constructor(model, view) { + this.model = model; + this.view = view; + + // View의 이벤트를 Model과 연결 + this.view.bindAddTodo(this.handleAddTodo); + this.model.addObserver(this.view.display); + } + + handleAddTodo = (text) => { + this.model.addTodo(text); + } + } + ``` + + + +* View + + ```js + // View: UI 표시와 사용자 입력 처리 + class TodoView { + constructor() { + this.input = document.createElement("input"); + this.button = document.createElement("button"); + this.list = document.createElement("ul"); + } + + display = (todos) => { + this.list.innerHTML = ""; + todos.forEach(todo => { + const li = document.createElement("li"); + li.textContent = todo.text; + this.list.appendChild(li); + }); + } + + bindAddTodo = (handler) => { + this.button.addEventListener("click", () => { + if (this.input.value) { + handler(this.input.value); + this.input.value = ""; + } + }); + } + } + ``` + + + + + +## MVP 패턴 + +MVC 패턴에서 model과 view 가 관찰자 패턴으로 커뮤니케이션 했다면, MVP패턴은 Presenter 계층이 중간에서 조정하는 역할을한다. + +* MVC + +``` +Model ──(Observer 패턴)─→ View + ↑ │ + └─────── Controller ──────┘ +``` + + + +* MVP + + ``` + Model ←→ Presenter ←→ View + ``` + + + +* 계층 별로 테스트 하기가 더 용이 + +* 개인적으로는 더 명시적이어서 개발 인지부하가 더 적을거 같다. + +* 수동적인 VIew 계층 (MVC에서는 데이터 조작에 직접 관여, MVP에서는 관여 여지가 적음) + + + + + + + + + + + + ### MVVM 모델 + + Mvvm 모델로 오면서, UI 개발자와 서버 개발자가 분리될 수 있었음 + + 코드를 살펴봤을 때는 MVC모델에서 Model의<->View의 관찰자 패턴의 ViewModel(presenter) <-> View의 관찰자 패턴으로 이동한 것 같다. + + + + MVP에서 Model과 View의 분리의 장점을 가져가면서도, 단방향 데이터 흐름을 만들 수 있는 것이 `킥`이다 + + + + 아래 코드에서 ViewModel이 View를 직접적으로 모르지만, `상태`가 업데이트되었음을 알리고, View에서 render() 를 통해 선언적으로 UI를 표현하는 방식이 react에도 많은 영향을 주지 않았나 싶다. + + ```js + class TodoViewModel { + private todos: string[] = []; + private subscribers: Array<() => void> = []; + + // ... 중요한 로직들 + + subscribe(callback: () => void): void { + this.subscribers.push(callback); + } + + private notifySubscribers(): void { + this.subscribers.forEach(callback => callback()); + } + } + + // View + class TodoViewMVVM { + private input: HTMLInputElement; + private list: HTMLUListElement; + + constructor(private viewModel: TodoViewModel) { + // DOM 설정... + + // 이벤트 바인딩 + this.input.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + const todo = this.input.value; + if (todo) { + this.viewModel.addTodo(todo); + this.input.value = ''; + } + } + }); + + // ViewModel 구독 + this.viewModel.subscribe(() => this.render()); + } + + private render(): void { + const todos = this.viewModel.getTodos(); + this.list.innerHTML = ''; + todos.forEach((todo, index) => { + const li = document.createElement('li'); + li.textContent = todo; + li.addEventListener('click', () => this.viewModel.removeTodo(index)); + this.list.appendChild(li); + }); + } + } + + ``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +