디자인 패턴과 뷰패턴(2)

minchan jung·2022년 3월 6일
0

1. 대체가능성 Polymorphism

  • 확장형이 확장되기 전을 대체할 수 있어야 함
  • Javscript 구현
  • prototype chain, chain 을 모두 조사해서 instance 동일하게 정의 내려줌

2. 내적일관성, 내적동질성

  • 태생을 그대로 유지 하는 것
  • 확장된 method 구현시, 구현체 instance method로 동작해야함
  • 언어 spec 지원 되어야 하며, 안될시 객체 지향 언어라 할 수 없음.
  • js 역시 prototype chain 으로 해결.

대체 가능성, 내적일관성 모두 객체 지향 언어의 필요 충분 조건
객체 지향 언어는 대체 가능성과 내적일관성을 유지한다 !
대체 가능성과 내적일관성을 지원 하는 언어는 객체 지향 언어이다 !

3. Design Pattern

  • Category - 생성패턴, 구조패턴, 행동패턴
  • 학습 분류 : 캡슐화, 다형성, 변화율, 역할모델, 객체 관계
  • 중점 학습 point : 격리를 통한 역할모델 분리 !

1. 모든 코드는 변한다!

  • 비즈니스변화
  • 연관 라이브러리 변화
  • 호스트측 변화 (chrom update)

=> 대부분 통제불가 요소 !

2. 제어문 기반의 알고리즘 문제

  • 수정하면 전체가 컴파일됨
    => 알고리즘이 변화한 부분만 수정하고 나머지는 건드지 않는다!!
    => 최대한 개별 알고리즘을 함수로 분리한다.
    => if를 제거 하는것이 큰 테마라 할 수 있다.

    이 간단한 주제가 아키텍쳐 이하 모든 디자인 패턴의 원형적인 목표라 할 수 있다.

제어 case 마다 함수로 분리 (절차 지향)

if case =1  =>  case1함수 
else if case =2  => case2함수, 공통함수
else if case =3  => case3함수, 공통함수 

문제1) 공통함수, 공통data 사용

data share 문제 발생
공통함수에 의해 로직 수정시 전체 케이스를 살펴야함

문제2) case가 변경될때

case 추가시 or 삭제시 or 변경시에도 전체 코드를 살펴야함

해결) 객체 지향

함수가 가리키는 data를 은닉하고 캡슐화
함수의 data 직접 사용 및 직접 coupling 불가능하게 만들어야 함

  • 객체 지향으로 바꾼다 와 같은 의미 (1970년대 이미 설계되었다)
  • 참고) 폰노이만 기반 머신은 객체지향, 절차지향 모두 절차 지향으로 바꿔서 해석한다 !, sub routine 으로 통신
  • 컴퓨터는 가능한데 인간은 불가능하다!!
  • 객체 지향으로 설계 하라!

3. 알고리즘 분화시 OOP 선택 solution (case에 대한 처리)

상속위임 vs 소유위임

  1. 상속위임 : 내부 계약관계로 추상층에서 공통 요소를 해결하고 상태를 공유할 수 있음 (객체 지향)

    • 내부의 사정을 다 아는 객체로서 존재하게 만든다.
    • 좁은 의미의 통신처리 필요할때
    • 속도 효율 개선(속도 와 메모리는 교환한다 측면)
  2. 소유위임 : 외부 계약관계로 각각이 독립적인 문제를 해결하며 메세지를 주고 받는 것으로 문제를 해결함 (절차 지향)

    • 독립된 개체간의 protocol 통신 약속이 필요함
    • 통신해야할 protocol을 정의 하는일 대부분이라 생각해도 좋다.
    • 연결해야할 대상이 넓으면 넓을 수록 유리하다.
    • 규모가 클 수록 절차적 소유위임이 유리하다 (library)
    • 범용적 solution
    • 메모리 효율 개선
    • GoF DP 방향성
  • 모든 문제는 절차 지향 과 객체 지향으로 나누어서 해결 가능함

Run time vs Compile time(static time)

  1. 언어마다 다름
  2. js
    • base code load time : static time
    • load 이후 : Run time
  3. java
    • compiler
    • 메모리 load 후 : run time env

상대적임을 알자!
코드 실행 시점에 먼저 필요한 것들은 static time에서 실행
코드 실행 이후 다시 위의 코드가 static timer라할 수 있음

실행시점 과 정의시점

  1. 실행시점(변하는 부분) : run time
  2. 정의시점(변하지 않는 부분) : static time

실행시점에 필요한 변하지 않는 부분을 생각하며 static 으로 구현한다.
static으로 부터 위임 받아서 실행시점에서 실제 동작을 구현한다.

상속위임에 의한 case 처리

Template Pattern by 객체 지향형 내적 일관성의 원리

  • if case 만큼의 상속 객체를 만들어서
  • case 는 정적(static) time에서 load되게 한 후,
  • 그 객체를 선택하는 권한은 run time으로 돌린다.

case 만큼의 객체가 만들어져야 하므로 메모리 비용 발생이 높다

소유위임에 대한 case 처리

1. Strategy Pattern by 함수형!

  • if case 만큼 정의된 함수를 필요 시점에 소유한 함수에게 위임한다
  • 정의시점에서 실행시점에 필요한 함수의 외부 주입이 필요하다.
  • 같은 객체로 실행시점에 필요 함수만 바꿔주며 재활용 할 수 있다. (메모리 사용 효율적)

abc언어 : 식 vs 문

식 : 메모리에 직접 관련된 값, 메모리에 적재되는 값(or not), 값=식, 식=연산, 연산하는 함수 = 값
연산은 값과 교환, 값 = 연산 = 함수는 메모리 직접 관련되어 등가성을 지닌다해도 무방
메모리가 충분하면 연산을 줄이고 값으로 만들어 메모리 적재후 참조
메모리가 부족하면 연산으로 처리함.
컴퓨터 세계에서 분리되지 않는다.
값, 연산, 함수는 식이라는 등가성을 지닌다.
문 : compiler, interpereter가 프로그램을 돌릴때 참조하는 hint, 메모리에 사라짐

  • 참고 : 디자인 패턴으로 탄생한 프로그램은 연산을 많이 하도록 설계되기 때문에 느리다. (70년대 GoF: 메모리 사용률을 줄이는 방향으로 설계)
  • 디자인 패턴을 깨어야할 환경 (게임, layer층이 여러 단계인 고도화된 그래픽 환경)

2. command pattern (문을 기억해두는 행위)

  • command 객체 : 다음번에 똑같은 제어문(command) 실행할 수 있는 장치를 기억(구워 두는) 객체
  • invoker 객체 : (기억된)command 객체를 실행 시켜주는 객체

통합 soln for 소유 위임

  • 변화 할 수 있는 if case 로직을 runtime 환경에서 관리
  • cammand + strategy 패턴을 함께 사용
  • case 마다의 구현 함수는 static time에서 실행
  • 실행시점 선택위임 : 실제 실행 시점에서 코드는 run time 환경에서 command 패턴을 활용한 선택적 외부 주입으로 해결한다.

ROUTER

연산을 값으로 바꾸는 것

  • router table : case 마다 필요한 함수를 matching 하고 있는 테이블
  • 선택과 연산을 제거하고, 경우의 수에 대응하는 결과를 나오도록 만들어준다.
  • host의 변화율은 관리를 할 수 없으므로, case를 모아둔 테이블로 관리하겠다는 차선책이라 볼 수 있다.
  • 케이스가 늘어날 경우 hash table에 add만 해주면 되도록 설계

4. 상속위임, 소유위임 동일 logic

"상태에 대한 분기는 사라지지 않는다"

  • 을 제거해서 으로 바꿀수 있는 객체화 작업
  • 위의 격리 구간 = static time(정의시점)
  • run time(실행시점)에 경우의 수를 공급

실행시점으로 분기를 옮길 떄의 장단점

장점
1. 정의 시점에 모든 경우를 몰라도 됨
2. 정의 시점에 그 경우를 처리하는 방법도 몰라도 됨
일정한 통제 범위 내에서 확장가능한 알고리즘설계 가능

단점
1. 실행 시점에 모든 경우를 반드시 기술해야 함
2. 실행 시점마다 알고리즘의 안정성을 담보해야 함
매 호스트코드마다 안정성을 따로 담보해야 함.
(모든 케이스가 값으로 정의되지 않으면 Run time error, context error 발생, frame전체가 약해짐)

확장성이 떨어지고 안정성이 중요하다면 기피해야함
Runtime 불안정성은 Static-time 불안정성에 연결된다

Trade off 고려 사항

  • 확장성을 확보해서 runtime 불안정성이 올라간다.
  • 확장성이 없어지지만 static time (compile)단계에서 코드의 안정성이 확보된다. runtime과 독립적이다.

캡슐화 패턴 : Factory, Builder pattern

  • runtime 불안정성을 커버하기 위한 새로운 패턴 도입
  • 패턴을 위한 패턴화

5. TDD

  • 원래 코드의 내구성이 좋을때 하는 것
  • 내구성 결정 요인 = 격리
  • 그렇지 않을 경우 테스트 코드는 무용지물
    - 문을 추가해서 테스트 케이스를 통과하게 만들어야 할때, 격리가 제대로 되지 않은 나쁜 설계를 했다고 할 수 있다.

상속위임 code ex

// Github.js
// 상속 위임 
const Github = class{ // 정의 시점 - 변하지 않는 부분 
  constructor(id, repo){
    this._base = `https://api.github.com/repos/${id}/${repo}/contents/`;
  }
  // 공통 부분 (template method)
  load(path){
    const id = 'callback' + Github._id++;
    const f = Github[id] = ({data :{content}})=>{
      delete Github[id];
      document.head.removeChild(s);
      this._loaded(content) //위임 부분
    };
    const s = document.createElement('script');
    s.src = `${this._base + path}?callback=Github.${id}`;
    document.head.appendChild(s);
  }
  _loaded(v){ throw 'override!' ;} // Hook (내부 hook을 통한 template pattern)
}
Github._id = 0; 

// ImageLoader.js
const ImageLoader = class extends Github{ // 실행시점 = 변하는 부분 
  constructor(id, repo, target){
    super(id, repo);
    this._target = target;
  }

  // 위임 구현 
  _loaded(v){
    this._target.src = 'data:text/plain;base64, ' + v;
  }
}


// index.js 
// 실행 context
const s75img = new ImageLoader(
  'minchjung',
  'imgloader',
  document.querySelector('#a')
);

s75img.load('035d19cfbebc3b8479939736a5f7bfe4.jpg');

소유 위임 code ex

//Github.js
// 소유 위임 
const Github = class{ // 정의 시점 - 변하지 않는 부분 
  constructor(id, repo){
    this._base = `https://api.github.com/repos/${id}/${repo}/contents/`;
  }
  load(path){
    if(!this._parser) return // parser 존재x kill
    const id = 'callback' + Github._id++;
    const f = Github[id] = ({data :{content}})=>{
      delete Github[id];
      document.head.removeChild(s);
      console.log(this._parser)
      this._parser[0](content, this._parser[1][0]) //위임 부분, 실행 부분 (invoker)
      // this._parser[1] = ele 여야 하지만, [ ele , src] <-로 불러와짐 
    };
    const s = document.createElement('script');
    s.src = `${this._base + path}?callback=Github.${id}`;
    document.head.appendChild(s);
  }
  setParser(f, ...arg){this._parser = [f, arg];} // 위임 객체 ( command pattern, command 객체 생성 )
  // 실행시점 = 변하는 부분 
}
Github._id = 0; 



// index.js
const el = (name) => document.querySelector(name);
const img =(v,el)=>el.src = 'data:text/plain;base64, ' + v;
const loader = new Github('minchjung', 'imgloader');
loader.setParser(img, el('#a'));
loader.load('035d19cfbebc3b8479939736a5f7bfe4.jpg')

소유위임 with router code ex

// Github.js
// 소유 위임 - with router
const Github = class{ // 정의 시점 - 변하지 않는 부분 
  constructor(id, repo){
    this._base = `https://api.github.com/repos/${id}/${repo}/contents/`;
  }
  load(path){
    if(!this._parser) return // parser 존재x kill
    const id = 'callback' + Github._id++;
    const f = Github[id] = ({data :{content}})=>{
      delete Github[id];
      document.head.removeChild(s);
      console.log(this._parser)
      this._parser[0](content, this._parser[1][0]) //위임 부분, 실행 부분 (invoker)
      // this._parser[1] = ele 여야 하지만, [ ele , src] <-로 불러와짐 
    };
    const s = document.createElement('script');
    s.src = `${this._base + path}?callback=Github.${id}`;
    document.head.appendChild(s);
  }
  setParser(f, ...arg){this._parser = [f, arg];} // 위임 객체 ( command pattern, command 객체 생성 )
  // 실행시점 = 변하는 부분 
}
Github._id = 0; 

// Loader.js
// loader 이자 router 실행 시점에서 선택위임
const Loader = class{
  constructor(id, repo){
    this._git = new Github(id, repo);
    this._router = new Map;
  }
  add(ext, f, ...arg){
    ext.split(',').forEach(v=> this._router.set(v,[f, arg]));
  }
  load(v){
    const ext = this._v.split('.').pop();
    if(!this._router.has(ext)) return ;
    this._git.setParser(...this._router.get(ext));
    this._git.load(v);
  }
}

// index.js

const el = (name) => document.querySelector(name);
const img =(v,el)=>el.src = 'data:text/plain;base64, ' + v;
const loader = new Github('minchjung', 'imgloader');
loader.setParser(img, el('#a'));
loader.load('035d19cfbebc3b8479939736a5f7bfe4.jpg')

코드스피츠-디자인패턴과 뷰패턴

0개의 댓글