객체지향프로그래밍(2) - 객체지향프로그래밍(OOP)의 필요성?

객체지향프로그래밍 입문

Featured image

🔚 짧게 하는 복습

✅ 1. C++의 기본 문법에 익숙해지자.

✅ 2. iostream, std::cin, std::cout에 익숙해지자.

✅ 3. 이제는 malloc, realloc, free 대신 new, delete에 익숙해지자.

혹시 기억이 안 난다면, 다시 돌아가자


아직은 객체가 무엇인지, 갑자기 왜 C++을 배워야 했는지 영문도 모른 채 끌려왔을 것이다.

우리가 C언어든 자료구조를 배우면서 느낀 것은, 항상 모든 새로운 발명은 새롭게 생겨난 수요에 의해 발생했다.

객체지향 프로그래밍이라는 기술도 분명 어떠한 수요에 의해서 발생했을 텐데, 이 수요를 말로만 전달하면 아무래도 와닿지 않는다.

그래서 우리가 직접 느껴보는 시간을 가지겠다.


간단한 게임 구현

혹시 포켓몬스터라는 닌텐도 게임을 아는가?

필자는 이 게임을 굉장히 좋아하는데 그중 베틀을 굉장히 좋아한다.

그래서 이 베틀에서 착안한 게임을 간단하게 베껴서 게임을 만들고 싶어졌다.

규칙은 아래와 같다.

3마리의 포켓몬은 각각 물, 불, 풀의 상성을 가진다.

물 -> 불, 불 -> 풀, 풀-> 물 순으로 서로에게 유리한 상성을 가진다.

또한 유리한 상성이 공격할 때는 1.2배의 데미지가 들어가고 불리한 상성이 공격할 때는 0.8배의 데미지만 들어간다.

포켓몬은 각각 이름, 체력, 공격력, 방어력, 이동속도를 가진다.

기본적으로 한쪽 포켓몬의 체력이 0 이하로 떨어질 때까지 턴제로 진행된다.

처음 공격은 이동속도가 높은 쪽이 선공권을 가지게 된다.

포켓몬은 각각 스킬을 가지며, 스킬은 위력과 명중률을 가진다.

자신의 순서가 되면 포켓몬은 스킬을 사용할 수 있다.

스킬을 사용 시 위력 X 공격력만큼의 데미지를 줄 수 있다.

하지만 명중률만큼의 확률을 가지고, 공격이 빗나갈 수도 있다.


구현

#include<iostream>
#include <cstdlib>
#include <ctime>

enum {Fire, Water, Grass};


struct skill_info {
	int power;
	int accuracy;
};

struct pokemon {
	std::string name;
	int type;
	double hp;
	int attack;
	int defend;
	int mobility;
	struct skill_info skill;
};


pokemon arr[3] = { {"브케인", Fire, 450, 45, 10, 20, {8, 50}},{"리아코", Water, 500, 35, 20, 10, {5, 55}},{"치코리타", Grass, 550, 40, 30, 0, {5, 60}} };

void make_pokemon(pokemon* pomon, const int opt) {
	pomon->name = arr[opt].name;
	pomon->type = arr[opt].type;
	pomon->hp = arr[opt].hp;
	pomon->attack = arr[opt].attack;
	pomon->defend = arr[opt].defend;
	pomon->mobility = arr[opt].mobility;
	pomon->skill.power= arr[opt].skill.power;
	pomon->skill.accuracy = arr[opt].skill.accuracy;
}

void print_pokemon(pokemon pomon) {
	std::cout << "이름 : " << pomon.name << std::endl;
	std::cout << "속성 : " << pomon.type << std::endl;
	std::cout << "체력 : " << pomon.hp << std::endl;
	std::cout << "공격력 : " << pomon.attack << std::endl;
	std::cout << "방어력 : " << pomon.defend << std::endl;
	std::cout << "이동속도 : " << pomon.mobility << std::endl;
	std::cout << std::endl;
}

int make_random() {
	srand(time(NULL));  // 시드 설정

	// 0부터 100까지의 범위에서 난수 생성
	int random_number = rand() % (101);

	return random_number;
}

double check_type(int type1, int type2) {
	if ((type1 == Fire && type2 == Water) || (type1 == Water && type2 == Grass) || (type1 == Grass && type2 == Fire)) {
		return 0.8;
	}
	else if ((type1 == Fire && type2 == Grass) || (type1 == Grass && type2 == Water) || (type1 == Water && type2 == Fire)) {
		return 1.2;
	}
}

void attack(pokemon src, pokemon* dest) {
	if (make_random() <= src.skill.accuracy) {
		dest->hp -= (src.attack * src.skill.power) * check_type(src.type, dest->type);
	}
}

int main() {
	pokemon my_pomon;
	pokemon enem_pomon;

	std::cout << "나의 포켓몬과 붙고싶은 상대 포켓몬을 고르시오(중복 불가) : 0. 브케인, 1. 리아코, 2. 치코리타" << std::endl;

	int opt1, opt2;

	std::cin >> opt1 >> opt2;

	make_pokemon(&my_pomon, opt1);
	make_pokemon(&enem_pomon, opt2);

	print_pokemon(my_pomon);
	print_pokemon(enem_pomon);

	int my_turn = 0;
	int enem_turn = 1;
	int turns;

	if (my_pomon.mobility > enem_pomon.mobility) {
		turns = 0;
	}
	else {
		turns = 1;
	}

	while (1) {
		if (turns % 2 == my_turn) {
			attack(my_pomon, &enem_pomon);
		}
		else{
			attack(enem_pomon, &my_pomon);
		}

		std::cout << my_pomon.name << "의 체력" << my_pomon.hp << " " << enem_pomon.name << "의 체력" << enem_pomon.hp << std::endl;


		if (my_pomon.hp <= 0) {
			std::cout << "You lose!"<<std::endl;
			break;
		}

		if(enem_pomon.hp <= 0) {
			std::cout << "You win!" << std::endl;
			break;
		}

		turns += 1;
	}
}

구현에 관한 자세한 설명은 생략하도록 하겠다.

지금까지 C언어 구현에 익숙해졌다면, 충분히 따라올 수 있는 내용이라고 생각한다.


결과창

나의 포켓몬과 붙고싶은 상대 포켓몬을 고르시오(중복 불가) : 0. 브케인, 1. 리아코, 2. 치코리타
1 0
이름 : 리아코
속성 : 1
체력 : 500
공격력 : 35
방어력 : 20
이동속도 : 10

이름 : 브케인
속성 : 0
체력 : 450
공격력 : 45
방어력 : 10
이동속도 : 20

리아코의 체력232 브케인의 체력450
리아코의 체력232 브케인의 체력250
리아코의 체력-36 브케인의 체력250
You lose!


객체지향 프로그래밍이란?

지금까지 흐름으로 보았을 때, 새로운 코딩 방법이 왜 필요한지 의문이 생긴다.

그런데 아래와 같은 수요가 생긴다고 해보자.

  1. 포켓몬을 500마리 정도 만들어서 게임을 만들고 싶다.

  2. 500마리 정도를 만들었는데, 게임이다 보니 포켓몬의 속성을 디테일하게 더 추가하고 싶다. (경험치, 특수공격, 특수방어 등)

  3. 각각의 속성마다 공격에 추가 효과를 부과하고 싶다.

처음의 1번 수요는 500마리를 추가하면 되니까, 사실 크게 어려움이 없다.

그런데 2번과 3번을 보자, 500마리를 겨우 추가했더니 속성이 추가되고 속성마다 공격 함수도 바뀌어야 한다…?

500마리마다 하나하나 속성도 추가해야 하고 속성마다 if를 걸어야 하고, attack 하나면 충분했는데 속성만큼의 다른 attack도 만들어야 한다.….

이런 문제가 생기는 이유는 간단하다.

일정한 절차로 처리될 함수를 구현하는 것에만 집중되어 있고, 실생활의 특징을 반영하지 못하기 때문이다.

이렇듯 프로시져(함수) 단위로 일정한 절차에 맞게 구현하는 방식절차지향 프로그래밍이라고 한다.

그리고 지금까지 우리가 한 모든 코드는 절차 지향적 프로그램이었다.

하지만 우리가 구성하고자 하는 게임은 조금 다르다.

조금 더 실제 세계를 반영하여, 포켓몬 한 마리 단위상태와 행동을 정리하여 구현하고 싶다. 또, 조금 더 프로그램의 유지보수를 쉽고 간단하게 하고 싶다.

이 약점을 보완할 새로운 패러다임이 필요했고 객체지향 프로그래밍(Object Oriented Programming)이 도입된다.

이는 각 구현 대상의 상태와 행동으로 마치 하나의 단위로 다루겠다는 프로그래밍 방법이고, 최종적으로 유지보수의 용이, 시스템의 안정적 구현에 목표를 두고 있다.

다음 시간에는 이 객체지향 프로그래밍의 예제를 살펴보며 자세히 알아보자.


📖 오늘의 핵심(다 알기 전까지는 넘어가지 말자❗)

✅ 1. 객체지향 프로그래밍의 도입 배경을 이해한다.

✅ 2. 절차지향 프로그래밍과 객체지향 프로그래밍의 정의를 이해한다.

⚠️ 절차지향 프로그래밍과 객체지향 프로그래밍은 대립하는 개념이 아니다! (자주 오해하는 개념이다)