"상호작용하는 객체의 집합"
객체지향 프로그램은 어떠한 목적을 이루기 위해 협업하도록 구성되어지는 객체들의 집합이다.
그리고 이 협업을 위해 메시지를 교환하는 관계이다.
코드 재사용을 증가시키고, 유지보수를 쉽게 만드는 장점을 얻기 위해서 '캡슐화', '다형성', 상속' 등을 이용하며 객체들을 연결시켜 프로그래밍 하는 것이라 할 수 있다.
reference: "Why C++ is not just an Object Oriented Programming Language: Session 3" / Bjarne Stroustrup, 1995, OOPSLA in Austin Texas
언어 또는 기술이 다음을 직접 지원한다면 객체지향 언어라 할 수 있다.
추상화(Abstraction)
: 현실 세계의 개체를 추상화하기 위해 클래스나 객체를 제공하는 것
상속(Inheritance)
: 이미 존재하는 것으로부터 새로운 추상화를 만들어 내는 것
실시간 다형성(Run-time Polymorphism)
: 수행 시간에 바인딩 할 수 있는 어떠한 형태를 제공하는 것
상태(state): 객체는 데이터를 가지고 있다.
: 특정 시점에 객체가 가지고 있는 정보의 집합으로 객체의 구조적 특징을 표현한다.
행위(behavior): 객체는 행위의 집합을 가지고 있다.
: 외부의 요청 또는 수신된 메시지에 응답하는 활동이다. 상태를 변경하거나 다른 객체에게 메시지를 전달할 수 있다.
행위를 통해 다른 객체와 협력하므로 외부에 가시적(public 접근)이어야 한다.
식별(identity): 객체를 구분할 수 있는 식별을 갖는다.
: 어떤 객체를 다른 객체와 구분하는데 사용하는 객체의 속성이다. 객체는 상태가 변경될 수 있기에 동일한 속성을 가져도 서로 다른 객체로 식별이 가능하다.
객체지향 설계를 한다는 것은 '적당한 객체를 찾는 것'이라고도 할 수 있다. 객체의 크기/종속성/유연성/성능/진화 가능성/재사용 등을 고려하며 시스템을 구성할 적당한 객체로 분할하는 것이 중요하다.
이때 무엇(what)을 정의한 객체 간 의사소통(collaboration)이 중요하다. 적당한 객체를 찾는 것의 출발점은 객체의 책임(responsibility)을 결정하는 것이고, 이 책임의 결정은 협력이라는 문맥에서 수행한다. 협력은 "메시지를 결정하는 문제"이다. 결국 객체의 책임을 찾는 것은 메시지를 결정하는 것이다.
캡슐화는 데이터를 캡슐로 싸서 외부의 접근으로부터 데이터를 보호하는 객체지향 특성이다. c++에서 캡슐의 역할을 하는 것이 클래스이며 class 키워드를 이용하여 작성한다. 클래스는 객체를 정의하는 틀이며, 객체는 클래스라는 틀에서 생겨난 실체(instance)이다. C++ 클래스는 멤버 변수들과 멤버 함수들로 이루어지며, 멤버들은 캡슐 외부에 공개하거나(public), 보이지 않게(private) 선언할 수 있다. 이중 공개된 멤버들만 외부 객체들이 접근할 수 있다.
멤버변수들은 외부에 보이지 않게(private) 선언하여 외부에 노출시키지 않는 것이 좋다. 대신 일부 멤버 함수들은 외부에 공개하여(public), 이 멤버 함수들 통해서 멤버 변수에 간접적으로 접근하게 된다.
멤버 함수들을 통해서 멤버 변수에 간접적으로 접근하게 만드는 것은 단순히 getter/setter 메서드를 의미하는 것은 아니다. 객체의 속성(데이터)와 행위(메서드)를 하나로 묶고, 실제 구현 내용 일부를 외부로부터 감추어 은닉하는 것이다.
이를 인터페이스(interface)와 구현(implementation)의 분리라 할 수 있다. 인터페이스는 객체의 외부에서 접근할 수 있고, 구현은 객체 내부에서 접근할 수 있다.
객체가 수행하는 책임이 아닌 내부에 저장할 데이터에 초점을 맞춤. ex) getter/setter의 남용
데이터를 묻지 말고, 기능을 실행해달라고 요청하라는 것이다. 대표적으로 getter/setter의 남용은 이러한 원칙을 깨는 것이다. 객체가 수행하는 책임이 아닌 내부에 저장할 데이터에 초점을 맞추는 것은 C언어의 구조체를 사용하는 것처럼 절차지향적 코드의 방식과 다를 게 없다. 절차지향적 코드에선 (1) 정보(information)을 얻는 것과 (2) 그 정보를 바탕으로 의사 결정을 내리는 것이 분리되어 있다. 의사결정을 내리는 로직을 사용자에게 감추고, 사용자는 본인에게 필요한 것만 요구하도록 한다.
모듈 간 결합도를 최소화하라는 뜻이다. 이를 다른 표현으로 하자면 '하나의 객체가 다른 객체를 지나치게 알게 하지 말라'는 뜻이다. 객체들간의 협력 경로를 제한하면서 결합도를 낮추라는 뜻이다. '협력 경로 제한'이라는 것은 멀리 떨어져 있는 낯선 객체에 메시지를 보내지 말라는 것이고, 쉽게 말해 '.' 또는 '->'를 줄이라는 뜻이다.
객체지향언어에서 상속이란, 자식이 부모로부터 유산을 물려받는 개념이라기보다는, 자식이 부모의 유전자를 물려받는 것과 유사하다.
상속은 구현된 코드의 재사용성을 높여서 "소프트웨어 생산성"을 높이고, 계층구조의 표현을 가능케 한다.
다형성은 하나의 기능이 경우에 따라 서로 다르게 보이거나 다르게 작동하는 현상을 일컫는다. (넓은 의미의 )다형성은 크게 연산자 중복, 함수 중복과 함수 오버라이딩(상속 관계)을 통해 실현할 수 있다.
이를 통해 프로그램의 흐름 제어(flow of control)를 단순화 할 수 있다.
네 가지 대표적인 다형성 타입이 있다.
다형성은 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있게 한다.
이러한 특성으로 다음과 같은 장점들이 나타날 수 있다.
여러 타입의 객체를 하나의 타입으로 관리하니 유지보수가 좋다.
ex) 추상클래스를 참조하는 방식(하나의 타입으로 관리)
ex) 구체클래스의 변경과 추가, 확장을 클라이언트는 몰라도 된다.
메서드의 매개변수(인자)로 상위 클래스, 추상 클래스, 인터페이스 등이 온다면 그 하위 클래스, 인터페이스를 구현한 클래스 등이 인자로 들어갈 수 있어 좀 더 유연한 프로그래밍을 할 수 있다.
확장성이 좋은 코드를 작성할 수 있고(대표적으로 strategy 패턴), 결합도가 강하지 않은 프로그래밍을 할 수 있다(코드 수정 부분을 훨씬 줄일 수 있다.).
다형성을 구현하는 방법은 다양하지만, 메시지에 응답하기 위해 실행될 메서드를 컴파일 시점 또는 실행 시점에 결정한다는 공통점이 있다.