Font size: +

객체 지향에 대한 잡설.

​공부 중에... 손에 잡히지도 않고

같이 후디니 배우시는 분들 중에 프로그래밍 아예 처음 시작하시는 분들도 있는 것 같고 해서, 제가 생각하는 객체 지향이란 것에 대해서 끄적거리고자 합니다.


1. 객체 지향은 뭐지?

넌지시 이야기 한 적이 있지만, 세상에 존재하는 모든 것들(물질적인것, 추상적인것)들을 프로그래밍으로 표현하기 위해 도입한 개념입니다.

물질적인 것을 예시로 들자면, '사람', '선풍기'등, 생물, 무생물을 가리지 않으며, 추상적인 것을 예시로 들자면 '감정' 등이 있을 겁니다.

그렇게 '우리가 표현하고 싶은 어떤 것'을 '객체'라는 단어로 표현합니다.

영어로는 Object라고 하는데, 흔하게 '물체'라고 번역하지 않은 것은 추상적인 것 역시 표현하기 위함이라고 생각합니다.


그렇다면 한 객체의 구성 요소에는 어떤 것들이 있을까요.

크게 보면 다음과 같이 나눌 수 있습니다.


객체의 상태

객체의 행동


저 두 가지를 다른 말로 바꿔 보겠습니다.


객체의 상태 = 변수

객체의 행동 = 함수


이렇게 보니 생각보다 단순한 개념 같지 않나요?


'어떠한 것을 프로그래밍으로 표현하고 싶은데, 그것의 상태와 행동을 한 데 모아서 객체라고 부르자'

이게 객체입니다. 이것을 쓰자는게 객체 지향입니다.


2. 객체 지향 핵심 키워드

단순히 이 정도에서 끝나면 객체 지향의 개념이 이해하기 어렵다는 말이 안 나왔을 겁니다. 그렇다면, 객체 지향이 왜 이해하기 어렵다는 말이 나오는 걸까요.

처음보니까 그런겁니다.


처음 보시는 분들이기 때문에, 객체 지향 하면 반드시 따라오는 몇 가지 키워드에 대해서 생소하실 테죠.

클래스, 캡슐화,  정보은닉, 상속, 다형성. 이 정도가 아닐까 합니다.


하나하나 살펴보도록 합시다.


2.1) 클래스

흔히 사용하는 비유가 있습니다. 붕어빵 틀이 클래스고 붕어빵이 객체(혹은 클래스의 인스턴스)다.

저 한 마디로 이해를 하셨다면 더 할 나위 없이 좋겠지만, 객체라는 개념 조차 생소한데 저렇게 던져줘서 이해하면 천상 프로그래머죠. 후디니가 아니라 Cpp를 잡으셔야 할 지도...


최대한 쉽게 클래스의 특징에 대해서 나열해 보자면 다음과 같습니다.


a) 클래스에서 찍어낼 '각각의' 객체의 행동과 상태에 대한 정보를 담고 있는 것.

b) A 클래스에서 찍어낸 A클래스의 객체가 '모두 공유하는' 상태를 담고 있는 것

c) 클래스의 객체가 아닌, '클래스 자체의 행동'에 대한 정보를 담고 있는 것.


즉, 객체를 만들 때 사용 할 규칙과 객체의 행동을 정해 놓는 것이 클래스입니다.

붕어빵 틀과 붕어빵예로 드는게 가장 빠를 듯 하니, 붕어빵 틀에 비유를 하자면, 붕어빵 틀이 클래스고, 붕어빵은 클래스 객체 입니다. 

붕어빵 틀은 계속 붕어빵을 찍어낼 수 있죠? 그게 클래스와 클래스 객체의 관계와 일치합니다.


그렇다면, 찍어낸 붕어빵을 한 번 봅시다.

요즘엔 붕어빵에 팥 말고 다른것들도 들어간다고 합니다. 슈크림, 초코크림, 그 외 풀빵과 어울리는 크림류들.

붕어빵은 붕어빵인데, 안에 들어간 것들만 다르죠? 이렇게 클래스 객체별로 다른 상태를 가질 수 있게 해 주는 것을 '맴버 변수'라고 합니다.

비슷한 개념으로, 찍어낸 붕어빵이 하게 될 행동(먹힌다거나)을 정해놓은 것이 '맴버 변수'입니다.


추가적으로, 클래스에는 각각의 클래스 객체들이 아닌, 클래스 자체가 행동과 상태를 가질 수도 있습니다. 아무리 붕어빵을 찍어내도, 붕어빵 틀이 바뀌는 경우는 없지요? 

그 붕어빵 틀 자체의 성질과 행동을 정하는게 클래스 변수와 클래스 함수입니다. (static, 정적 변수, 함수라고 부르는게 일반적이긴 합니다.)

이것도 무슨 소리인지 감이 잘 안 잡히실텐데요, 이 경우는 붕어빵은 잊으시고, 사람이라는 클래스가 있다고 가정을 해 봅시다.


사람의 체온은 특별한 이상이 있지 않은 한 36.5도를 유지해야 합니다. 한 마디로, 모든 '사람'클래스의 객체는 체온 = 36.5라는 값을 가지게 됩니다.

그런데, 이걸 모든 객체가 따로 가지고 있을 필요가 있을까요? 절대 그럴 필요가 없습니다. 이 경우엔 클래스 전체가 공유하는 변수를 하나 마련하는게 좋습니다.

이럴때 쓰는게 클래스 변수입니다. 이 녀석은 클래스 객체 모두가 공유, 혹은 클래스 자체의 특징을 나타내는 변수라고 생각하시면 됩니다. 

마찬가지로 클래스 자체 혹은 모든 클래스 인스턴스가 공유하는 행동은 클래스 함수로 만들어 놓는게 좋습니다.


2.2) 캡슐화

캡슐화는 클래스, 클래스 객체끼리 할 일을 명확히 구분짓는 개념입니다.

여기서는 인명부와 사람 클래스 두 가지가 있다고 생각을 하는게 좋겠네요.


인명부 클래스의 객체는 인명부 그 자체입니다. 인명부에서는 지금 출석 인원이 몇 명인지, 총원이 몇 명인지 등등, 인적 사항에 관련된 일을 하는게 맞겠죠?


클래스의 '행동'에 초점을 맞춰 봅시다.

인명부 클래스가 '숨쉬기'같은 행동을 하는게 적절할까요? 당연히 아닙니다.

다른 예시로, '사람'클래스에서 인명부가 하는 일을 도맡아 하는건 적절할까요? 이 경우도 아닙니다.


클래스의 '상태'에 초점을 맞추면 어떻게 될까요?

인명부 클래스가 '체온'이나 '직업'을 가지는게 현명한 판단일까요?

'사람'클래스가 '총원'이나 '결석'등에 대한 정보를 가지는게 타당한가요?


아니죠. 상식을 벗어나는 일이라는 생각이 드네요.


즉, 각각의 클래스가 자신에게 관련된 상태와 행동만을 가지고 있어야 합니다. 이것이 캡슐화입니다.


3.3)정보 은닉

이건 간단합니다. 객체 외부로 보여줘도 되는 값이 있고, 보여주면 안되는 혹은 보여줄 필요가 없는 값은 안 보여주는게 정보은닉입니다.

사람은 모두 다른 DNA정보를 가지고 있습니다. 하지만 그걸 굳이 다른 사람에게 보여줄 필요가 있을까요? 일반적인 경우에는 전혀 일어나지 않는 일입니다.

선풍기를 예로 들어 봅시다. 미풍, 약풍, 강풍, 정지 버튼이 있습니다. 사실 이것만 가지고 있어도 선풍기를 쓰는 데엔 아무런 문제가 없습니다. 우리가 선풍기의 구동 원리를 자세하게 알아야 하나요?


보여줄 것만 보여주는 것. 이게 정보은닉입니다.


3.4) 상속

상속은 1개의 클래스가 아니라, 여러개의 클래스 끼리 관계른 나타내는 개념입니다.

B라는 클래스가 A클래스를 상속 받으면, B 클래스는 A클래스의 대부분의 상태와 기능을 활용할 수 있게 됩니다.

쉽게 말해서, 학생 클래스가 사람 클래스를 상속받는다 할 때, 사람 클래스도 숨을 쉬고, 학생 클래스도 숨을 쉴 수 있다는 이야기 입니다.

세부적으로는 조금씩 다르지만, 공통으로 가지고 있는 부분이 있는 클래스들의 경우, 공통으로 가지는 부분을 상위 클래스로, 세부적으로 다른 것을을 하위 클래스로 나누어 구현하게 됩니다.


즉, 공통된 부분을 가지는 클래스 끼리의 규칙이 상속입니다.

그런데, 자세히 살펴보니 공통된 부분을 제외하고 서로 다른 부분은 하위 클래스에서 새롭게 구현된 것들이죠?

즉, 상속받은 클래스는 기반 클래스에 비해 상태 혹은 동작이 더 많아지는 경우가 보통입니다.


3.5) 다형성

다형성은 쉽게 말하면 똑같이 생긴 애들이 다른 행동을 하게 만드는 기능입니다.

상속을 통해 구현되는 기능이라, 자세하게 설명하려면 상속에 대한 설명 역시 자세하게 해야 하지만, 여기서는 간단한 개념만 설명해 보도록 하겠습니다.


여러 사람들을 쭉 모아놓고, '너 지금 뭐 해?'라고 물어보면, 다들 다른 대답을 하죠? 그게 어찌 보면 다형성의 개념과 비슷합니다.



이 예시를 보시죠.

일단, 사람 클래스가 있고, 사람 클래스를 상속받는 초등학생, 중학생, 고등학생, 대학생, 직장인 클래스가 있다고 가정합니다.

그리고, 모든 클래스는 '숙제'라는 행동, 즉 함수를 가지고 있습니다.

잘 생각해 보면 초등학생, 중학생, 고등학생, 대학생, 직장인은 모두 사람입니다. 그렇기에 다음 대입이 성립합니다.

사람 = 초등학생;

하지만, 어떤 사람이 있을 때, 그 사람이 무조건 초등학생이라고 보장할 수는 없습니다. 즉, 다음 등식은 성립하지 않습니다.

초등학생 = 사람;

이 경우를 확장해서, 사람 클래스 배열에 사람을 포함하여, 초등학생부터 직장인 까지 모든 클래스를 하나씩 저장합니다. 이런 모양이 되겠네요.

사람[0] = 사람;

사람[1] = 초등학생;

....

사람[5] = 직장인;

이 경우, 변수형 자체는 '사람'형이라는 것에 주목하셔야 합니다. 딱 봤을때는 똑같이 생긴 애들이라는 겁니다. 이제 각각의 배열 맴버에게 '숙제'라는 명령을 내립니다.

과연 각각의 배열 원소는 '사람'클래스의 '숙제'를 실행할까요, 각각 다른 '숙제'를 실행할까요?

#include <iostream>
using namespace std;

class Human{
public:
	virtual void DoYourJob(void)
	{
		cout << "사람은 열심히 산다." << endl;
	}
};

class ElemStudent:public Human{
public:

    //override 키워드는 c++ 11에서 추가되었습니다.
	virtual void DoYourJob(void) override
	{
		cout << "초등학생이 숙제를 한다" << endl;
	}
};

class MidStudent:public Human{
public:
	virtual void DoYourJob(void) override
	{
		cout << "중학생은 공부를 한다." << endl;
	}
};

class HighStudent:public Human{

public:
	virtual void DoYourJob(void) override
	{
		cout << "고등학생은 공부하느라 죽어난다." << endl;
	}
};

class UnivStudent:public Human{
public:
	virtual void DoYourJob(void) override
	{
		cout << "대학생은 술 먹느라 죽어난다." << endl;
	}
};

class Worker:public Human{
public:
	virtual void DoYourJob(void) override
	{
		cout << "직장인은 돈 버느라 죽어난다." << endl;
	}
};

int main(void)
{
	cout << "시작" << endl;
	Human* humanArr[6];
	humanArr[0] = new Human();
	humanArr[1] = new ElemStudent();
	humanArr[2] = new MidStudent();
	humanArr[3] = new HighStudent();
	humanArr[4] = new UnivStudent();
	humanArr[5] = new Worker();

	for (size_t i = 0; i < 6; i++)
	{
		humanArr[i]->DoYourJob();
	}

	int useLess;
	cin >> useLess;

	return 0;
} 

​비주얼 스튜디오 환경이 갖춰지신 분들은 직접 실행해 보시는걸 추천합니다. 다른 분들도 여기까지 했으면 대충 뭔 말을 하려는지 감은 오시죠? 그런 의미에서 결과는 딱히 기재하지 않습니다.


다형성을 만들 때, 등호를 기준으로 이렇게 생각하시면 쉽습니다.


(겉에 보이는 모습) = (실제 행동하는 양식);

명령은 겉에 보이는 모습을 기준으로, 행동은 실제 행동 양식에 맞춰서 합니다. 전 개인적으로 변수 선언 시 등호 앞 자료형을 정적 자료형, 등호 뒤 자료형을 동적 자료형이라 생각하고 사용합니다. 

VEX 언어 참고서
Git을 소스트리에 연동하기
Comment for this post has been locked by admin.
 

Comments