처음에 객체지향이라는 것을 배웠을 때에는
객체가 무엇인지, 클래스가 무엇인지, 캡슐화, 상속, 다형성
이러한 개념에 대해서만 배웠던것 같다.
학교에서 "객체지향 프로그래밍"이라는 수업도 들었는데,
사실 아직까지도 이 객체지향이라는 개념이 잘 와닿지 않는것 같다.
아마 나 말고도 많은 사람들이 이 개념이 중요하다는 것은 알지만
아직까지 잘 이해가 가지 않는 사람이 많을 것이다.
하지만 지금은 먼저 이 복잡하고 거대한 객체지향이라는 산에 오르기 전에
왜 이 객체지향을 사용하고, 또 우리가 왜 배워야 하는지 살펴보자.
객체지향 프로그래밍이라는 것이 어떤 배경에서 생겨났고,
현재 이 객체지향 프로그래밍을 왜 사용하는지를 알게 된다면
앞으로 객체지향 이라는 개념을 이해하고, 또 사용하는데 큰 도움이 될 것이다.
객체지향 프로그래밍(OOP, Object-Oriented-Programming)은 앨런 케이가 대학원을 다니는 중에 만들었다고 한다.
그가 이 객체지향을 만든 이유는 코드의 복잡성과 관련이 있다.
SW는 변화가 불가피하다.
소프트웨어 개발에서 가장 중요한 부분은 무엇일까?
코딩을 처음 배웠을 때에는 그냥 주어진 문제를 코드로 해결하기만 하면 되는 것인줄 알았다.
하지만 이전학기에 배웠던 소프트웨어 공학 수업 내용처럼 단순히 기능만 구현했다고 해서 개발이 끝나는 것이 아니다.
대부분의 SW는 새로운 프로그램을 개발하는 것 보다
개발한 프로그램을 유지보수하는데 훨씬 많은 비용이 든다고 한다.
SW는 변화가 불가피하기 때문이다.
기술은 계속 발전하고, 환경도 계속 바뀐다.
경우에 따라서는 SW에 적용되는 법이 바뀌기도 하며 비즈니스 상황이 바뀌기도 한다.
이러한 상황에서 그냥 코드를 설계없이 막 짜면, 결국은 스파게티 코드가 되어 SW에 변화를 가하기가 어려워진다.
즉 객체지향이라는 개념은 이렇게 변화가 많은 SW를 잘 설계하기 위해 만든것이다.
(이런걸 유지보수성이 좋고, 이해하기 좋은 코드라고 하는 것이다.)
먼저 앨런 케이가 객체지향을 만들기 전에 주된 프로그래밍 방식이었던 절차지향적으로 간단한 코드를 짜면서 살펴보자.
절차적으로 코드를 짜보면??
먼저 절차지향이 잘못되었다는 것을 말하는 것이 아니다.
객체지향이 절차지향보다 뛰어나다고 주장하는 것도 아니다.
이 내용은 앨런 케이가 어떤 배경에서 객체지향을 개발했는지 보기위해 간접적으로 살펴보는 것이다.
(함수형 프로그래밍이라는 개념이 나오고 있지만 일단은! 아직 거기까지 생각하지 말자.
자바와 객체지향을 어느정도 공부하고 나서 살펴보는 것이 더 좋을것같다.)
초기 프로그래밍은 절차적 프로그래밍이었다.
절차적 언어는 c를 생각해보면 흐름에 따라 순서대로 실행이 된다.
c를 어셈블러로 컴파일한 어셈블리 코드를 보면 branch나 jump 등을 해서 다른 코드로 이동할뿐,
전체적인 흐름은 계속해서 순서에 따라 실행이 된다.
(인터럽트나 시그널 등 갑자기 들어오는 건 여기서 생각하지 말자.)
즉 흐름에 따라 계속 연산을 하는데, 시스템이 복잡해지다보면 반복되는 연산이 생기게 된다.
이때 이런 연산들을 재사용하기 위해 만든 것이 바로 함수이다.
예를들어 커피를 주문하는 (발로 짠 간단한) 코드가 있다고 해보자.
#include <stdio.h>
#define COFFEE_COST 5000
void order_coffee(int* customerMoney, int* cafeMoney, int* coffeeNum);
void calculate_money(int* customerMoney, int* cafeMoney);
void make_coffee(int* coffeeNum);
int main() {
int customerMoney = 10000; // 고객이 가지고 있는 재산
int cafeMoney = 100000; // 카페가 가지고 있는 재산
int coffeeNum = 10; // 남은 커피 개수
order_coffee(&customerMoney, &cafeMoney, &coffeeNum);
return 0;
}
void order_coffee(int* customerMoney, int* cafeMoney, int* coffeeNum) {
if (*customerMoney >= COFFEE_COST && *coffeeNum > 0) {
calculate_money(&customerMoney, &cafeMoney);
make_coffee(&coffeeNum);
}
else if (*customerMoney < COFFEE_COST){
printf("돈이 부족합니다!\n");
}
else {
printf("오늘 커피는 모두 팔렸습니다! 다음에 또 와주세요.\n");
}
return;
}
void calculate_money(int* customerMoney, int* cafeMoney) {
*customerMoney -= COFFEE_COST;
*cafeMoney += COFFEE_COST;
}
void make_coffee(int* coffeeNum) {
*coffeeNum--;
printf("커피 제조를 시작합니다...\n");
sleep(5);
printf("커피 나왔습니다!\n");
return;
}
여기서 우리는 자연스럽게 함수라는 것을 이용해서 코드를 짰지만, 만약 함수 없이 짜게 된다면 어떻게 될까?
그렇다면 우리는 커피를 시키는 코드를 실행해야 할때 마다
고객의 재산을 빼고, 카페의 재산을 더하고, 커피 개수를 빼고, 커피를 제조하는
코드가 중복이 될 것이다.
하지만 이러한 각 행동(기능)을 함수로 묶어주면,
매번 코드를 중복해서 작성할 필요 없이 함수를 한번 호출하는 것으로 그 행동을 수행 할 수 있게 된다.
여기서 좀더 살펴보면, 각 함수로부터 어떤 관계가 나온다는 것을 볼수 있다.
가장 주된 관계는 다음과 같다.
호출하는 caller, 호출되는 callee
하지만 프로그램이 커지고 코드가 복잡해지면 함수간에 연쇄적으로 호출이 일어나고,
어떤 함수가 공유 데이터를 건드렸는지 파악하기가 점점 어려워진다.
추가나 수정이 필요한 경우
사실 위의 코드는 간단해서 금방 바꿀수 있긴 하지만,
커피의 종류가 추가된다거나, 할인 쿠폰이 생긴다거나, 잠시동안 이벤트를 추가하는 등
또다른 행동을 추가한다고 해보자.
그러면 행동을 추가하면서 데이터에 다른 연산을 가하기도 하고,
데이터의 구조가 바뀌기도 하면서 그와 관련된 함수들도 전부 수정을 가해주어야 한다.
예를들어 할인 쿠폰이 생기면 커피 가격에 할인을 가해서 가격이 떨어지게 되는데
그렇게 되면 다음과 같은 함수들을 수정해 줘야 한다.
order_coffee()
에서 고객의 돈과 커피 가격을 비교하는 부분에서 커피의 할인 가격이 들어가야 한다.calculate_money()
에서 커피의 할인 가격을 더하고 빼야한다.
물론 위의 코드는 매우 단순한 코드이기 때문에 흐름을 파악하기 쉽고 수정도 쉬워보일 수 있다.
하지만 함수가 수백, 수천개가 되고 코드의 양이 많아지다보면
이 흐름을 파악하기도 어렵고 수정을 가하기도 어려워진다.
또한 각 함수끼리 연쇄 호출을 하면서 동작하기 때문에 특정 함수나 부분만을 따로 떼와서 재사용하기가 어렵다.
이러한 배경에서 앨런 케이는 이러한 코드의 복잡성을 줄일까 고민하면서 만든 것이 바로 객체지향 프로그래밍이라는 것이다.
앨런 케이가 생각한 아이디어
앨런 케이는 어떻게 하면 소프트웨어의 복잡성을 줄일수 있을까에 대해 고민하다가 생명체를 구성하는 기능적 기본단위인 세포들로부터 아이디어를 얻었다.
생물은 여러 세포들로 이루어져 있으며, 각 세포들이 각자의 역할을 수행하며 협력한다. 각 세포는 자기 일을 스스로 알아서 하며, 역할이 끝나면 스스로 소멸하고 다른 세포로 대체되기도 한다.
생물학에 대해 잘 모르기 때문에 자세한 내용은 다음 링크를 참고하자!!
https://www.joongang.co.kr/article/23949743#home
앨런 케이는 생물학을 전공하였기 때문에 이러한 세포들간의 협력에서 아이디어를 얻었다고 한다. 다음은 실제로 앨런케이가 했던 말이다.
"I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages."
저는 오직 메시지로만 커뮤니케이션 할 수 있는 생물학적인 세포같은 무언가 또는 단일 네트웤상의 각각의 컴퓨터들에 대해 생각하고 있었습니다.
실제로 생물학에서 각 세포를 Object라고 부르며, 군집한 전체 집합에 속하는 하나를 이야기할때 사용한다.
앨런 케이는 이러한 개념을 소프트웨어에도 적용할 수 없을까 고민하였고, 그렇게 탄생한 것이 바로 객체지향 프로그래밍이다.
객체지향 프로그래밍
즉 객체지향 프로그래밍은 생물이 여러 세포로 이루어지는 것처럼,
시스템을 여러 객체로 이루어지도록 한것이다.
이 객체는 각자 자신에게 주어진 역할이 있고, 그 역할에 따라 행동한다.
그런데 만약 자신의 역할이 아닌 작업이 필요한 경우, 객체는 그 작업을 자신이 수행하지 않는다.
대신 다른 객체에게 요청을 하여서 다른 객체에게 그 작업을 맡기는데, 이때 다른 객체에게 '메시지'를 보내서 요청한다.
여기서 주의할 점은, 객체는 다른 객체에게 메시지를 전송해서 요청만 할뿐, 그 행동을 하도록 강제할 수 없다는 것이다.
또한 그 객체가 행동을 어떻게 수행하는지에 대해서 알수 없다.
즉 시스템의 기능이 여러 역할로 쪼개어 지고, 이 역할을 각 객체가 가지게 된다.
그리고 객체간에 메세지를 전송하여서 해당 객체가 특정한 행위(작업)을 하도록 요구한다.
"객체지향의 사실과 오해"라는 책에서도 다음과 같은 내용이 나온다.
시스템은 역할과 책임을 수행하는 객체로 분할되고
시스템의 기능은 객체 간의 연쇄적인 요청과 응답의 흐름으로 구성된 협력으로 구현된다.
객체지향이란 시스템을 서로 상호작용하는 자율적인 객체들의 공동체로 바라보는 것이다.
유미와 세포들, 실제 자바 코드 예시 추가로 내용 작성!!
출처
- https://velog.io/@eddy_song/oop-starting-point
- https://velog.io/@eddy_song/alan-kay-OOP
- https://medium.com/javascript-scene/the-forgotten-history-of-oop-88d71b9b2d9f
- https://mono9.tistory.com/3
- https://onlyfor-me-blog.tistory.com/367
- https://www.youtube.com/watch?v=JS-Pr6EmNj4&t=294s
- http://everdeenoop.blogspot.com/2017/01/java-bdd.html
- 객체지향의 사실과 오해
'OOP' 카테고리의 다른 글
값 객체(VO), 뭔지 알고 쓰자! 그리고 쓰는 이유는?? (2) | 2023.10.29 |
---|