프로그래머스의 백엔드 데브코스에서 했던 학습을 처음부터 복습하며 기록을 남겨보려 한다. 가장 먼저 객체지향 프로그래밍에 대해 학습했다.
Java가 1990년대에 객체지향 언어로 등장했다. 직관적으로, 객체지향 프로그래밍은 프로그램을 객체들과 그들 간의 메세지로 구성한 프로그램이다. 그렇다면 Java로 작성한 프로그램은 객체지향 프로그래밍인가? 그렇지 않다. 객체지향적 철학, 사고에 맞게 프로그램을 만들어야 객체지향 프로그래밍이 완성된다. Python, C++ 등의 다른 언어, 그리고 Javascript 또한 객체지향 사고를 접목해 객체지향 프로그래밍을 만들 수 있다.
OOP의 등장 배경
객체지향 프로그래밍은 흔히들 프로그램이 거대화하면서 등장했다고 한다.
거대한 프로그램을 만드는 것은 어떤 방법으로든 가능하다. 그 방식을 하나의 큰 덩어리로 만든다면, 당연하게도 유지보수가 어렵다. 최초의 프로그램 개발자가 아닌 다른 사람이 레거시를 이어 받아 관리하는 과정에서 더 많은 리소스가 들어간다. 그에 대한 해답으로 잘게 나눠 각 객체들의 기능이 상호작용하도록 구성해 만드는 프로그램이 객체지향 프로그래밍이다.
명령들을 절차적으로 수행하는 컴퓨터이기에 객체지향 프로그래밍의 등장은 절차적 프로그래밍의 이후라는 인식이 있는 것 같다. 과연 객체지향 프로그래밍 이전의 패러다임은 절차적 프로그래밍이 유일했던 것이었을까?
객체부터 알아보자
객체 - 가상 세계 주민
객체와 클래스, 인스턴스는 모두 다르다. 이는 개발 공부하기 시작하고 첫 관문인 OOP를 공부하면서 가장 어려웠던 개념이다.
객체는 개념적인 용어다. 현실 세계의 문제를 해결하기 위한 프로그램을 구현하기 위한 가상 세계의 구성이 객체다. 어떠한 언어에 구애받는 것도 아니고, 구현 이전에 개념의 문제이다.
객체지향의 사실과 오해라는 책에 자세히 나와있는 설명처럼, 이상한 나라의 앨리스로 예시를 들어보자.
앨리스는 버섯의 한쪽을 먹어 키가 줄고, 버섯의 반대쪽을 먹어 키가 다시 커져 본인의 키를 조정한다.
이를 나타내는 프로그램을 만든다고 가정하면, 생각할 수 있는 객체는 앨리스와 버섯이다. 앨리스와 버섯의 기능, 또는 행동을 정의하자면 앨리스는 버섯을 먹는 행동과 버섯의 반대쪽을 먹는 행동이 있다.
현실 세계를 생각하자면 버섯은 아무 행동을 하지 않는다. 앨리스가 버섯을 먹으면 행위의 주체는 앨리스이기 때문이다. 하지만 버섯의 입장에선 앨리스가 버섯을 먹은 만큼 줄어야한다.
현실이 아닌 가상 세계에선 버섯 또한 한쪽이 먹힌다는 행동과 반대쪽이 먹힌다는 행동을 가질 수 있다. 즉, 객체는 현실 세계의 규칙을 반드시 따라야하는 것은 아니다. 객체는 가상 세계의 주민인만큼 가상 세계를 개발하는 개발자가 정의한대로 정의가 되는 개념적인 존재다.
물론 객체를 잘 정의하고 바람직하게 설계하는 이론 또한 존재한다. 객체지향 사실과 오해는 이를 위한 좋은 내용을 담고 있다. 여담이지만, 객체지향 존재론(Object Oriented Ontology)라는 철학적 개념이 있다. 인간이 주체에서 멀어져 모든 것을 객체로 보고 객체의 독립성과 자율성에 집중해 세상은 객체들과 관계가 전부가 되는 것이다. 일반적으로 주체가 되는 인간이라는 존재의 비인간 객체의 존재에 대한 우위를 부정하는 개념이다. 가상 세계를 이루는 객체지향 프로그래밍에도 어울리는 철학이라는 생각이 든다.
클래스와 인스턴스 - 객체의 구분
객체의 설명을 보자면, 자연스럽게 클래스와 인스턴스로 구현된 코드가 상상이 될 수도 있다. 객체의 설명이 마치 클래스와 인스턴스에 대한 설명 같은 그런 기분이 든다. 나는 이 부분이 헷갈렸다. 클래스와 인스턴스 또한 실체가 없는 가상의 존재인데, 객체와 무엇이 다른걸까?
클래스와 인스턴스는 기술적인 용어다. 추상적인 객체가 정의되면 그를 클래스와 인스턴스로 구현할 수 있다. 프로그램을 구성할 상상 속의 세계에서 정의된 객체들을 구현하자면, 객체들의 구분이 필요하다. 객체가 가지는 행동과 상태의 규칙을 타입으로 구분할 수 있다. 그렇게 정의된 객체의 타입이 클래스이다. 클래스에서 객체의 행동을 나타내는 영역을 메서드영역이라고 하고, 상태를 나타내는 영역을 필드영역이라고 한다.
class MyObject extends Object implements Runnable {
// Fields
private int a = 0;
// Method
public void run() {
a += 1;
}
}
정의한 클래스를 실제로 메모리를 할당하면, 그것이 인스턴스이다. 인스턴스를 변수에 할당하기 위해 타입을 정하자면, 당연히 그 타입은 클래스가 된다.
// 타입이 클래스인 변수 선언
MyObject obj;
// 인스턴스 생성. 메모리에 할당은 되었지만, 접근할 변수가 없는 상태
new MyObject();
// 인스턴스를 변수에 할당
obj = new MyObject();
다시 생각해보면, DBMS를 배울 때 엔티티와 실제 테이블과의 관계라고도 할 수 있지 않을까?
정의된 객체의 틀이 클래스이고, 그 틀로 생성되어 실제 프로그램에서 업무를 수행하는 것이 인스턴스다. 물론 구체적인 견해가 다양하다. 형체가 없는 만큼 이해도의 폭도 다를텐데, 어느 선 정도만 일치한다면 이에 대해 대화할 때 혼동하는 일은 없지 않을까 싶다.
OOP 이전의 프로그래밍
앞서 던져졌던 질문처럼, 객체지향 프로그래밍 이전엔 모두가 절차적 프로그래밍을 따랐을까? 객체지향 프로그래밍은 그 유행을 일으킨 1960대 쯤의 Simula와 Smalltalk이 시초가 됐다고 여겨질 수도 있다. 이런 언어들 이전에 작성된 프로그램들은 객체지향 프로그래밍이 아닌걸까?
관점마다 다르다고 생각한다.
함수들의 절차로 이루어진 절차형 프로그래밍은 절차를 제어하는 goto문으로 프로세스를 제어한다. 프로그래밍 언어에 대해 다시 생각해보자. C, 혹은 그보다 로우 레벨의 언어들은 어셈블리 언어의 집합이다. 어셈블리 언어로 된 명령들을 묶어 기능들로 재구성한다. 높은 수준의 언어를 사용하는 개발자 입장에선 그 기능의 내부 구현에 대해서 몰라도 상관없다. 마치 객체지향 프로그래밍을 설명할 때 특징과 비슷한 느낌이다.
프로그래밍 언어 또한 자료 구조와 연산 절차를 추상화하고 손에 잡히지 않는 논리적인 개념을 구상해 점점 사용자가 사용하기 쉽게 발전했다고 생각한다. 컴퓨터가 발명되고 개발자들은 가상 세계를 구현하기 위해서 자연스럽게 객체들을 생각하며 살았을지도 모른다. 그것을 객체라고 부를 생각을 못했을 뿐이다.
기술은 문제와 이론이 정의되면 그 후로 폭발적으로 발전한다고 생각한다. Simula와 Smalltalk 등의 언어들이 OOP의 인기를 시작했다고 할 수도 있겠지만, 지금의 OOP는 프로그래밍을 위한 필수적인 요소가 아닌가? OOP가 메인스트림으로 자리매김한 시점은 Java의 등장이라고 생각한다.
Java는 모든 것이 객체다. SOLID, 디자인 패턴 등 좋은 객체지향 프로그래밍을 작성하기 위한 이론들을 Java에 대입하기도 쉽다. 어느정도 합의된 이론도 있고, 논의도 활발해지며 새로운 이론이 탄생하기도 한다.
어셈블리부터 시작해 어셈블리어 명령 집합을 추상화한 고수준 언어들, 그리고 객체지향 언어들과 그로부터 파생된 이론들까지, 소프트웨어의 발전은 가상 세계에서의 논리적은 개념들이 적립되면서 같은 결을 따라 이루어진 것이 아닐까하는 생각이 든다. 객체의 개념 정의되기 이전부터, 객체지향을 위한 프로그래밍이 점진적으로 발전해 온 것이 아닐까?
물론 쓸데없는 나만의 잡생각이다.
객체지향 프로그래밍
다시 객체의 이야기로 돌아와서 큰 덩어리의 프로그램을 잘게 쪼개 작은 객체들로 만든다. 객체들을 정의하기 위한 기준은 행동이라고 생각한다. 행동, 또는 작은 기능을 객체 하나 하나가 수행한다. 그리고 그 객체들 간의 협력을 한다. 이것이 간단한 객체지향 프로그래밍의 요소이다.
프로그램을 하나의 회사로 여겨보면 이해가 더욱 쉽다.
회사의 구성원들은 각자 자신의 업무를 수행한다. 각 부서마다, 그리고 각 직급마다 업무의 범위와 내용은 다르다. 궁극적으로 구성원들이 업무를 수행하며 동시다발적인 결과를 생산하거나, 혹은 비동기적인 결과를 내놓는다.
구성원들은 자신의 업무에 책임이 부여된다. 구성원들을 객체라고 생각해보면, 객체들은 프로그램을 잘게 나눠 각자의 업무를 맡고, 객체는 부여 받은 업무에 책임을 갖는다. 비슷한 업무나 특정 부류의 업무들을 수행하는 객체들은 같은 부서에서 업무를 수행하며 하나의 목표를 가지고 협력하며 책임을 다한다. 그렇게 여러개의 부서들이 모여 회사의 이익을 위한 프로세스를 수행한다. 현실 세계라면 업무를 하는 주체는 구성원들이지만, 주체를 뺀 객체들로 이루어진 가상 세계의 업무는 더 다양한 객체들에 의해 이루어지고, 그건 구성원 뿐만 아니라 더 추상적인 업무의 개념을 지닌 부서, 그보다 더 추상적이고 궁극적으로 수행되는 업무의 개념을 지닌 회사가 있다. 객체의 업무들을 잘 분업화하고, 객체들이 협력할 수 있도록 프로그램을 구성하는 것을 객체지향 프로그래밍이라고 할 수 있다.
References
https://www.quora.com/What-came-before-object-oriented-programming
What came before object-oriented programming?
Answer (1 of 4): Prior to the object-oriented programming paradigm, there was the procedural programming paradigm. Both are types of imperative programming, in which the programmer expresses how the state of the system should be changed. Both procedural an
www.quora.com
OOP Before OOP with Simula
Early versions of Simula experimented with an entirely different kind of object-oriented programming.
twobithistory.org
프로그래머스 백엔드 데브코스
객체지향의 사실과 오해
'객체지향 프로그래밍' 카테고리의 다른 글
객체지향의 특성 - 추상화 (0) | 2024.05.22 |
---|---|
객체지향의 특성 - 상속 (0) | 2024.05.21 |
객체지향의 특성 - 캡슐화 (정보은닉) (0) | 2024.05.21 |
단일 책임 원칙 이야기 (2) - 책임의 범주 (1) | 2024.01.21 |
단일 책임 원칙 이야기 (1) - 책임이란 (1) | 2023.11.30 |