Object-Oriented Programming
프로그램 설계방법론이자 ‘개념’의 일종. 명령형 프로그래밍에 속한다.
”클래스는 객체이며, 구조체는 객체가 아닌 데이터의 집합”이라는 설명은 틀렸다..!
프로그램을 단순히 데이터/처리방법 으로 나누지 않고 수많은 '객체(object)'라는 기본 단위로 쪼개고 이들간의 상호작용으로 서술하는 방식이다.
객체란 하나의 역할을 수행하는 '메소드와 변수(데이터)'의 묶음으로 봐야 한다.
-
객체지향을 안쓰면?
→ 단순한 알고리즘이라면 몰라도, 조금만 복잡해져도 복구 자체가 불가능한 스파게티코드가 완성된다! 남은 물론 코드를 작성한 본인도 유지보수가 어려워진다.
-
객체지향을 사용해서 얻을 수 있는 장점
- 코드 가독성이 향상됨. → 이거 하나면 만사 OK..
- 중복 코드를 어느정도 제거할 수 있음.
- 자세한 장단점( ref. 나무위키)
- 데이터 클래스의 상속이라는 개념은 굉장히 뛰어나지만 마찬가지로 굉장히 복잡한 특성을 지니게 해준다. 이 OOP 특성 덕분에 면밀한 자료 분석[5], 개발시간 단축[6], 좀더 정확한 코딩[7]을 보증하지만 코드의 난이도가 급상승한다. 한 마디로 어려워진다. 특히, 다중 상속이 되면 엄청 복잡해진다. 그래서 대다수의 OOP 언어들은 다중 상속을 지원하지 않았고 실제 구현이 전혀 없는 껍데기인 인터페이스만 추가로 상속할 수 있게 했는데, 이게 또 코드의 재사용성을 현저하게 떨어뜨리는 문제가 있어서[8] 최근엔 믹스인이나 트레이트같이 다중상속을 할 수 있는 방법을 찾고 있다. 상속이 복잡하게 얽혀 소스 분석이 어려워진 상태는 라자냐 코드라고 곧잘 불리는 편.
- 클래스는 오로지 관련 데이터만을 정의하기 때문에, 한 클래스의 인스턴스가 수행될 때 다른 프로그램의 데이터를 절대로 건드릴 수 없게 된다. 덕분에 높은 시스템 보안을 제공하고, 자료 훼손을 방지하는 효과가 있다. 하지만 public 변수를 남발해 버리면... 더 이상의 자세한 설명은 생략한다. 당연하지만, 이는 프로그램 내에서의 각 인스턴스간 내부적인 접근의 범위를 제한하는 것이다. 외부 프로그램의 메모리 접근 문제와는 다른 범주. 외부에서의 메모리 접근까지 차단한다면 메모리 변조는 어떻게 된단 말인가. 애초에 운영체제에서 메모리 관리도 못하게 될 것이다. Visual Studio 6.0의 C++는 데이터베이스의 자료 처리 시 변수를 모두 public으로 처리했었다. 이 이유는 데이터베이스의 필드가 50개이면 get과 set 메소드를 모두 구현해주면 100개의 메소드가 만들어져야 했기 때문이다. 변수 50개와 메소드 100개를 다 150개를 키보드로 쳐야 한다.[9] 생각만 해도 일할 기분이 안난다. 이건 Java도 마찬가지다. 변수의 get과 set 메소드 구현은 어찌됐든 굉장히 귀찮은 작업이다. 데이터의 은닉도 중요하지만 프로그래머의 손가락 건강도 생각해 봐야 한다. C#은 2.0부터 프로퍼티 하나만 선언해 주면 되기 때문에 큰 문제는 없다.구체적인 해당 사례를 알 수 없으니 원문을 남겨두지만, C++ 컴파일러라면 어느 것이든 상속과 타입 선언, 오버로딩 등을 사용하면 public 변수를 사용하는 것과 비슷한 코스트로 메소드를 사용한 입출력을 구현할 수 있다. 메소드가 없으면 클라이언트의 변화무쌍한 갈아엎기에 대한 대응력이 떨어져서 오히려 더 피곤해질 위험이 높은 코드다.
- 클래스의 정의는 최초로 생성한 프로그램뿐 아니라 다른 OOP에서도 똑같이 사용될 수 있다. 그리고, 이런 이유로 네트워크에 쉽게 분산 사용이 가능하지만 프로그래머에게는 아주 힘든 노력을 강요한다. 네트워크 통신이라는 것은 호환성을 위해 7bit ASCII 코드로 전송한다. 1960년대 만들어진 통신기기를 사용하는 곳도 아직 있기 때문이다. 이 때문에 Quarter-Print, Base64나 UTF-8 같은 것이 만들어진 것이다. 결국 객체도 7bit ASCII 코드로 전송을 해야 한다. 메모리상의 객체 정보를 ASCII 코드화 하는 것을 직렬화(Serialization)라고 하고 ASCII 코드를 다시 객체화 하는 것을 역직렬화(Deserialization)라고 한다. 하지만 마법처럼 그냥 되는 게 아니고 "직렬화 인터페이스"를 프로그래머가 직접 구현했을 때만 가능하다. Java를 비롯한 대다수의 객체 지향 언어들은 직렬화-역직렬화 인터페이스를 기본적으로 제공하고 있다. 이러한 아이디어를 발전시킨 것이 CORBA와 MS의 COM/DCOM/COM+이다. 최근에는 SOAP나 JSON, XML-RPC 등 텍스트 기반의 직렬화 기술도 많이 사용된다. Java에는 Java Runtime끼리 통신하기 위한 RMI(Remote Method Invocation)도 지원한다.
- 데이터 클래스 개념은 언어에 정의되지 않은 새로운 데이터 형식을 프로그래머가 임의로 정할 수 있도록 해준다.
- 캡슐화와 격리 구조 설계로 인한 성능 하락이 있다. 거의 대부분의 객체지향 언어에서 기능을 묶으면 결국 함수 호출이 추가로 들어가거나 계산식 중간에 포인터 연산 등이 필요해지며, 멤버 함수 같은 경우 어느 객체의 함수인지 지정해야 하기 때문에 추가 포인터 크기와 연산 비용이 들어간다. 인라인 함수와 컴파일러 최적화(특히 RVO(Return Value Optimization)) 등의 방법으로 어느정도는 격차를 줄여주나 역시 그냥 절차적 프로그래밍보다는 무거워진다.
- 개념을 기준으로 나누다보니 반복 연산이 컴퓨터 친화적이지 않고, 특히 배열 자료구조를 적용하기 힘들어진다. 객체 하나하나를 따로 캡슐화시키고 상속시 부모만 같으면 자식의 종류를 신경쓰지 않다보니 각자의 메모리 크기가 달라지며, 결국 고정된 연속 메모리에 담을 수가 없게 된다. 메모리 할당을 배열로 하지 못하게 되니 따로따로 생성하게 되는데 이렇게 각각의 객체의 생성과 파괴가 반복되면 메모리 단편화라는 문제가 생기게 된다. 가비지 컬렉션 기능이 만들어진 이유 중 하나. 또 연속 메모리를 쓰기 힘들어진다는 건 캐시의 효율적 사용에 큰 문제가 생긴다는 뜻이기도 해서 성능 격차는 더 벌어지게 된다. 이를 해결하는 코딩 패러다임으로 DOP(Data Oriented Programming)가 있고 구현 방법으로 메모리 풀이 있다.
- 객체 하나하나를 따로 나누는데 주력하다보니 서로 비슷한 처리를 하는 코드가 서로를 건드릴 수 없게 되었고 이를 해결하기 위해 getter(접근자: 값을 반환하는 메소드), setter(설정자: 필드에 값을 저장하는 메소드)사용이 너무 많아졌다. 이 과정에서 캡슐화가 깨지고 그냥 public으로 공개한 경우나 마찬가지인 상태가 되어 의미가 퇴색되었고, 다른 프로그래밍 패러다임이 필요해졌다. AOP(Aspect Oriented Programming)는 모든 코드 하나하나를 별개의 객체로 분리하기보다 '어떤 일을 어디서 처리하는가' 에 더 중점을 두어 큰 범위로 묶어주어 모듈화 효율을 개선시켰고, 현대 프로그래밍 언어와 코드들은 원본 객체 지향 방식을 그냥 그대로 적용하는 경우가 드물다.
-
하향식 프로그래밍에서, 상향식 프로그래밍으로
→ 네이밍 스페이스가 포화되고, 그로 인한 데이터의 오염이 발생되는 하향식 프로그래밍을 극복하기한 방안으로 OOP가 등장했다.
’프로그램'이라는 큰 문제를 해결하기 위하여 작은 여러개의 문제로 나누어 해결하던 하향식 프로그래밍에서 ‘작은 문제’를 해결할 객체를 만들고 이 객체들을 조립하는 상향식 프로그래밍으로 발전하였다. OOP가 제대로 적용되기 위해서는 독립성/신뢰성이 높은 객체로 만들어야 재사용성이 좋다.
-
디자인 패턴?
→ 객체 지향 프로그램이 복잡해지면서, 객체가 간결하게 정리될 필요성이 생겼다. 디렉토리를 깔끔하게 정리해야 하는 것과 마찬가지.
디자인 패턴이란, 프로그래밍 형식을 정하는 일종의 약속이며 이는 협업을 전제로 한 환경에서 특히 강조되고 있다.
[ https://namu.wiki/w/디자인 패턴 ]