코드스피츠 74 4회차
프로그램 짤때는 권한과 책임이 일치하게 짜야 된다. 권한과 책임이 일치하면 그것을 역활이라고 한다.
TETRIS
객체후보
- STAGE(현재 스테이지 정보)
- SCORE(점수 및 계산법)
- BLOCK(범용 블록정의) - 색깔, 회전
- 게임본체
- 범용 패널
- 시작화면
- 스테이지 종료
- 죽음
- 클리어
- 결과 화면
추상화
- 일반화(카테고라이즈) - 기준점은 역활이다
- 모델링 - 기억해야만 하는것
- 그룹핑 - 랜덤하게 묶이는것(권한)
직접 통신 방법 보다 인터페이스를 통해서 통신해야 된다. 수정에 열려있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const Stage = class{ init(listener){ this.listener = listener; } clear(){ this.stage = 0; this.next(); } next(){ if(this.stage++ < Stage.maxStage){ this.speed = 500 - 450 * this.stage / Stage.maxStage; this.listener(); } } [Symbol.toPrimitive](hint){ return `<div>Stage ${this.stage}</div>`; } };
Stage.maxStage = 20;
const Score = class{ init(listener){ this.listener = listener; } clear(){ this.curr = this.total = 0; } add(line, stage){ const score = parseInt((stage * 5) * (2 * line)); this.curr += score, this.total += score; this.listener(); } [Symbol.toPrimitive](hint){ return `<div>Stage ${this.curr} / ${this.total}</div>`; } };
|
블록 부모 클래스 정의
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const Block = class{ constructor(color){ Object.assign(this,{color, rotate:0}); } left(){ if(--this.rotate <0){ this.rotate = 3; } } right(){ if(++this.rotate <3){ this.rotate = 0; } } getBlock(){ throw 'override!'; } };
|
블록 자식클래스 정의
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const blocks = [class extends Block, ....];
class extends Block{ constructor(){ super('#f8cbad'); } getBlock(){ return this.rotate % 2 ? [[1],[1],[1],[1]] : [[1,1,1,1]] }
};
class extends Block{ constructor(){ super('#ffe699'); } getBlock(){ switch (this.rotate){ case 0:return [[0,1,0],[1,1,1]]; case 1:return [[1,0],[1,1],[1,0]]; case 2:return [[1,1,1],[0,1,0]]; case 3:return [[0,1],[1,1],[0,1]]; } }
};
|
위에서 자식 클래스에서 중복이 생김
부모 클래스에 추가작업
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const Block = class{ constructor(color){ Object.assign(this,{color, rotate:0}); } left(){ if(--this.rotate <0){ this.rotate = 3; } } right(){ if(++this.rotate <3){ this.rotate = 0; } } getBlock(){ return this.blocks[this.rotate]; } };
|
랜더러 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const Renderer = class{ constructor(col, row, base, back){ Object.assign(this,{col, row, base, back, blocks:[]}); } clear(){ throw 'override!'; } render(data){ if(!(data instanceof Data)) throw 'invalid data'; this._render(data); } _render(data){ throw 'override!'; } };
|
데이터 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const Data = class extends Array{ constructor(col, row,){ Object.assign(this,{col, row}); } cell(row,col,color){ if(row > this.row || col > this.col) throw 'invalid'; (this[row] || (this[row] = []))[col] = color; } row(row, ...color){ color.forEach((v, i)=> this.cell(row,i,v)); } all(...rows){ rows.forEach((v, i)=> this.row(i, ...v)); } };
|
랜더러 상속후 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const el = el=>document.createElement(el); const back = (s, v){ s.backgroundColor = v; };
const TableRenderer = class extends Renderer{ constructor(col, row, base, back, style){ super(col, row, el('table'), back) const {col, base, blocks} = this; base.style.cssText = style; let i = this.row; while (i--){ const tr = base.appendChild(el('tr')); const curr = []; let j = col; blocks.push(curr); while (j--) curr.push(tr.appendChild(el('td')).style); } } clear(){ this.blocks.forEach(curr=>curr.forEach(s=>back(s,this.back))); } _render(v){ this.blocks.forEach((curr,i)=>curr.forEach((s,j)=>back(s, v[i][j]))); } };
|
캔버스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const el = el=>document.createElement(el); const back = (s, v){ s.backgroundColor = v; };
const CanvasRenderer = class extends Renderer{ constructor(col, row, back, style){ super(col, row, el('canvas')) const {col, base, blocks} = this; base.style.cssText = style; Object.assign(this, { width:base.width = parseInt(base.style.width), height:base.height = parseInt(base.style.height), cellSize:[base.width/col,base.height/row], ctx:base.getContext('2D') }); } clear(){ this.ctx.clearRect(0,0,this.width, this.height); } _render(v){ this.clear(); const {col, ctx, cellSize:[w,h]} = this; let {row:i} = this; while (i--){ let j = col; while (j--){ ctx.fillStyle = v[i][j]; ctx.fillRect(j*w,I*h,w,h); } } } };
|
참조