✊ 필오의 개발일지
Back to Posts
2019년 3월 17일

코드스피츠80_OOP design with game (2)- 2. 모델 (베이스 레이어)

코드스피츠80_OOP design with game (2)- 2. 모델 (베이스 레이어)

코드스피츠 강의 정리록

생소한 도메인으로 배우는게 좋다. 익숙한 도메인들은 익숙한 처리방법으로 처리하기때문에 객체지향을 배우기 어렵다. 때문에 80기는 게임을 통해서 진행할 예정.


1. 저번시간의 코드에서 잘못된 점을 찾아보자.

1.1 베이스 클래스에 네이티브 지식이 포함되어있다.

const Block = class { static GET(type = parseInt(Math.random() * 5)){ return new Block(type);} constructor(type) { this._type = type; } get image(){ return 👉 `url('img/block${this._type}.png'`;} get type(){ return this._type} }

아키텍처는 코드에 가질 수 있는 역할과 가질 수 없는 역할, 기질 수 있는 책임과 가질수 없는 책임을 명시할 수 있고, 그것으로 코드가 바른지 안바른지 판단하는 기준표가 된다.


1.2 통제권을 가진 제왕이 존재한다. (1) - 블럭 위치값의 지식

👉 const data = []; ... return tid => { table = document.querySelector(tid); 👉 for (let i = 0; i < row; i++){ const r = []; data.push(r); 👉 for(let j = 0; j < column; j++) r[j] = Block.GET(); } table.addEventListener('mousedown', down); table.addEventListener('mouseup', up); table.addEventListener('mouseleave', up); table.addEventListener('mousemove', move); render(); };

cf__1. 프로시저적인 생각을 한다? 를 어디서 알 수 있냐면,

= 모든 연산을 data를 갖고 하려고 한다.

일반적으로 프로시저로 짜면 메모리를 적게 사용하게 된다.

블럭의 위치값

2차원 배열에 들어가는데, 블럭에 X값 Y값이 들어가있나?

객체지향은 내 할일이 아니면 위임하는 것이다.


1.3 통제권을 가진 제왕이 존재한다. (2) - 선택된 블럭의 지식

const selected = [], getBlock = (x, y) => {...} const down = ({pageX: x, pageY: y}) => { if(isDown) return; const curr = getBlock(x,y); if(!curr) return; isDown = true; 👉 selected.length = 0; 👉 selected[0] = startBlock = currBlock = curr; render(); }; const move = ({ pageX: x, pageY: y }) => { if (!isDown) return; const curr = getBlock(x, y); if (!crr || curr.type !== startBlock.type || !isNext(curr)) return; 👉 if (selected.indexOf(curr) == -1) selected.push(curr); 👉 else if (selected[selected.length - 1] == curr) selected.pop(); currBlock = curr; render(); }; const up = () => (👉 selected.length > 2 ? remove() : reset());
  1. selected를 사방에서 직접 컨트롤하고 있다.
  1. selected도 블럭이 선택되어있는지 아닌지에 대한 지식이기 때문에 Block의 지식이다.
  1. 우리는 완전히 독립되어있는 어떤 데이터를 바라보고 있는 프로시저를 짜서 해결하려고 한다.
  1. 끊임없는 상태에 대한 변화의 책임을 블럭이 갖고 있어야한다.

2. 프로시저를 객체지향으로 바꿔보자.

2.1 모델 (feat. 유틸)

2.1.1 심플한 책임

const Block = class { static GET(type = parseInt(Math.random() * 5)) { return new Block(type); } constructor(type) { this._type = type; } get image() { return `url('img/block${this._type}.png'`; } get type() { return this._type; } };

👇👇👇

const UTIL = { el: (v: string) => document.querySelector(v), prop: (...arg) => Object.assign(...arg), };
interface IItem { pos: (x: number, y: number) => void; select: (item: Item) => void; unselect: () => void; } const Item = class { static GET(type: number, x: number, y: number) { return new Item(type, x, y); } constructor(_type: number, _x: number, _y: number) { prop(this, { _type, _x, _y, _selected: false, _prev: null }); } get type() { return this._type; } get x() { return this._x; } get y() { return this._y; } get selected() { return this.selected; } get prev() { return this._prev; } pos(x: number, y: number) { // 어디로 움직일지 this._x = x; this._y = y; } select(item: Item) { // 어떤 아이템에 선택할지? this._selected = true; this._prev = item; } unselect() { this._selected = false; this._prev = null; } };
  1. 싱글 노드 링크드 리스트가 완성되었다.
  1. item 객체가 이 이상을 알게 되면, 권한 위반이 된다.
  1. 밖에는 캡슐화 된 메소드로 대화한다.

2.1.2 어려운 책임

좀 더 어려운 책임을 바라보게 하자.

const Item = class { ... isSelectedList(item: Item){ if(!this._prev) return false; if(this._prev === item) return true; else return this._prev.isSelectedList(item) } isBorder(item: Item) { return (Math.abs(this.x - item.x) < 2) && (Math.abs(this.y - item.y) < 2) } }

1. isSelectedList

selectedList에 포함되어있냐 아니냐를 판단하는 메소드

cf__2. 객체지향에서 책임의 범위와 자료구조
  1. 책임 범위를 축소하면 연산을 많이하게 된다.

    • 타클래스의 메서드를 호출할 경우도 있고, 본인의 연결되어있는 클래스를 호출하는 경우가 더 많다.
  2. 객체지향에서는 기본적으로 배열같은 어그리게이션(집합)을 쓰는 경우가 거의 없다.

    • 어그리게이션을 쓰면 어그리게이터에 대한 로직을 따로 짜야한다.
    • 코디네이터를 더 만들기 싫으면 본인 안에 링크드 리스트로 연결하는 수밖에 없다.
    • 컬렉터를 만드냐 안만드냐는 아키텍처상 중요한 요소이다.
    • 컬렉터를 추상화하는 것은 굉장히 어려운 것이다.
    • 통합된 자료구조를 안쓰게 되니까 다 연산으로 되어있는 자료구조를 쓰게 된다.
  3. 배열을 링크드 리스트로 치환한 것이다.

알고리즘의 태반은 자료구조로 되어있고, 자료구조의 태반은 다시 또 알고리즘으로 돌아가게 되어있다. 항상 메모리와 연산은 교환할 수 있다.

2. isBorder

나의 인접 셀을 파악하는 것도 나의 권한이다.

cf__3. 권한 + 책임 = 역할
  1. 우리가 책임을 부여하고 싶으면, 권한을 부여해야한다. 두개가 어긋나면 둘중 하나가 깨진다.

    • 객체지향에서 권한은 은닉화 되어서 내부 상태로 숨고, 책임은 표면화되서 외부 메소드를 드러나게 되어있다. 메소드는 캡슐화해서 표현된다. ex_ATM기기
    • 객체지향에서 메소드는 this라는 내부상태의 은닉되어있는 상태를 사용하고 있느냐, 추상화가 되어있는, 캡슐화 되어있는 메소드냐가 충족되어야 객체지향에서 메소드라고 부른다.
  2. 얼만큼 캡슐화되어야하는지는 대상에 따라 다르다.

내가 만약 프로그램을 짜서 공개되는 코드도 마지막에 은닉과 캡슐화를 처리하는 단계는 항상 머리속에 짱구를 그려야한다.

Previous2019 상반기 회고 + 하반기 다짐 (feat. 글또)
Next코드스피츠80_OOP design with game (2)- 1. 개요

Related

© 2025 Felix