CodeSpitz74_Part4(권한과 책임)

코드스피츠 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);
}
}
}

};

참조