class Character {
constructor(name, attack, defense, health = 100) {
this.name = name;
this.attack = attack;
this.defense = defense;
this.health = health;
}
execAttack(target) {
const damage = this.attack - target.defense;
// 최소 데미지는 1
if(damage > 0) {
target.health -= (this.attack - target.defense);
}
else {
target.health -= 1;
}
console.log(`${this.name} attacks ${target.name} for ${damage} damage`);
// 체력이 0 이하면 죽음
if(target.health <= 0) {
target.execDie();
return {die : true};
}
return {die : false};
}
execDie() {
console.log(`${this.name} has died`);
}
}
class Hero extends Character {
constructor(name, attack, defense, health, exp) {
super(name, attack, defense, health);
this.exp = exp;
this.skill = {}
this.level = 1;
}
execAttack(target) {
const isDie = super.execAttack(target);
if(isDie){
this.exp += 10;
if(this.exp >= this.level * 10) {
this.execLevelUp();
this.learnSkill()
}
}
}
execStatusUp() {
this.attack++;
this.defense++;
this.health += 5;
}
execLevelUp() {
this.level++;
}
learnSkill(){
if(this.level == 2) {
this.skill.doubleAttack = true;
console.log(`${this.name} learned double attack!`);
}
else if(this.level == 5) {
this.skill.tripleAttack = true;
console.log(`${this.name} learned triple attack!`);
}
}
}
class Slime extends Character {
constructor() {
super("slime", 30, 0, 50);
console.log(`${this.name} has spawned`);
}
execDie() {
super.execDie();
console.log(`${this.name} splitted!`);
}
}
function startGame() {
const slime = new Slime();
const hero = new Hero("hero", 10, 5, 100, 0);
hero.execAttack(slime);
}
startGame();
면접 준비를 한참 하는 중 '객체지향'이라는 주제로 질문을 많이 받았다.
본격적으로 개발을 공부할 때도 어려웠던 클래스와 객체지향의 개념.
이제는 어느정도 이해했다고 할 수 있을까? 내가 이해한 것을 바탕으로 간단한 게임형식의 코드를 작성해보았다.
이 로직은 게임의 시작시 캐릭터를 생성하는 기능을 담당한다.
몬스터인 '슬라임'과 주인공인 'hero' 인스턴스를 생성하는 간단한 파일이다.
객체지향은 크게 4가지정도의 특성을 가진다.
가장 상단에 있는 Character라는 클래스를 상속받아 Hero와 Slime이라는 클래스를 생성했다.
주인공과 슬라임은 모두 hp를 가져야 하고, 자신만의 이름을 가져야한다.
이러한 공통적인 특성을 부모클래스에 넣어놓음으로써 코드의 중복을 줄이고, 재사용성을 높일 수 있다.
Hero 클래스안에 execAttack() 메소드는 super에서 execAttack()을 호출한다.
이는 주인공은 공격시 타겟이 사망하면 경험치를 획득하는 '추가적인' 로직이 필요했기 때문이다.
주인공 인스턴스나 몬스터의 인스턴스에 똑같은 이름의 execAttack()이라는 메소드를 넣어놓되, 역할을 조금 다르게 만드는. 그러한 로직을 의도했다.
만약 이 클래스를 사용하는 누군가가 있다면 execAttack()내부 로직이 어떻게 되는지는 궁금하지 않다.
그저 instance.execAttact(타겟 인스턴스) 만 실행하면 해당 타겟을 공격한다 정도만 이해하면 된다.
상단의 Character클래스는 프로퍼티를 가진다. name이나, attack등을 가지는데 이는 private등으로 숨겨서 선언할 것이다. (여기서는 js만 적당히 사용했지만 타입스크립트나, 다른 언어에서는 접근 수준을 조정할 것이다.)
Character의 인스턴스를 한 번 생성했으면 이 프로퍼티들을 '직접' 조정할 수 없다.
변수에 직접 접근해서 값을 변경하는게 아니라 값을 변경하는 메소드를 만들고, 이를 public으로 풀어서 간접적으로 접근할 수 있도록 만든다.
이렇게 캡슐화를 진행해 '임의의 조정'을 불가능하게 만들고 클래스를 만든 사람의 의도대로만 프로퍼티가 변하도록 만들 수 있다.
간단한 게임 캐릭터의 생성 클래스를 만들면서 아이디어가 샘솟기 시작했다. 이 OOP를 이용해 게임의 다양한 부분을 구현할 수 있을 것 같다.
좋은 글 감사합니다. 자주 올게요 :)