좋은 아키텍처를 만드는 일은 객체지향 설계원칙을 이해하고 응용하는 데에서 출발한다.
OO란 무엇인가?
이는 얼버무리는 수준에 지나지 않는다.
실제 세계를 모델링 한다 라는 말이 무엇을 의미하며,
왜 우리는 그 방향을 추구해야 하는가?
이 답변이 전할하려는 의도는 OO는 현실세계와 의미적으로 가깝기 때문에
OO를 사용하면 소프트웨어를 좀 더 쉽게 이해할 수 있따는 데 있는듯 하다.
하지만 이 의도조차도 불분명하며, 그 정의가 너무 모호하다.
다시 말해 이 답변도 OO가 무엇인지를 설명해 주지 않는다.
OO의 본질을 설명하기 위해 세 가지 주문에 기대는 부류도 있는데
캡슐화, 상속, 다형성이 바로 그 주문이다.
이들은 OO가 이 세가지 개념을 적절하게 조합한 것이거나,
또는 OO 언어는 최소한 세가지 요소를 반드시 지원해야 한다고 말한다.
이 세 개념을 차례대로 살펴보자.
캡슐화
OO를 정의하는 요소중 하나로 캡슐화를 언급하는 이유는
데이터와 함수를 쉽고 효과적으로 캡슐화 하는 방법을 OO언어가 제공하기 때문이다.
그리고 이를 통해 데이터와 함수가 응집력 있게 구성된 집단을 서로 구분 짓는 선을 그을 수 있다.
구분선 바깥에서 데이터는 은닉되고, 일부 함수만이 외부에 노출된다.
이 개념들이 실제 OO언어에서는 각각 클래스의 private 멤버데이터와 public 멤버 함수로 표현된다.
사실 C언어에서도 완벽한 캡슐화가 가능하다.
.h 파일에서는 .c 파일 멤버에 접근할 방법이 전혀 없다.
구조와 함수가 어떻게 구현되어 있는지에 대해 조금도 알지 못한다. 이것이 바로 완벽한 캡슐화이며, 보다시피 OO가 아닌 언어에서도 충분히 가능하다. ←
꽤나 충격적..
c언어에서는 먼저 데이터의 구조와 함수를 헤더파일에 선언하고 구현파일에서 이들을 구현했다.
그리고 사용자는 구현 파일에 작성된 항목에 대해서는 접근할 수 없었다.
C++라는 형태로 OO가 등장했고, C가 제공하던 캡슐화가 깨졌다.
사용자 측에서 멤버변수를 알게되었다. 나중에 접근제한자를 도입함으로 써 불완전한 캡슐화를 보완하기는 했지만. 임시 방편이었다. 멤버 변수를 변경하게 되면 다시 컴파일 해야한다..
자바와 C#은 헤더와 구현체를 분리하는 방식을 모두 버렸고,
이로 인해 캡슐화는 더욱 심하게 훼손되었다.
따라서 OO가 강력한 캡슐화에 의존한다는 정의는 받아들이기 힘들다.
실제로 많은 OO언어가 캡슐화를 강제하지 않는다.
OO프로그래밍은 프로그래머가 충분히 올바르게 행동함으로써 캡슐화된 데이터를 우회해서 사용하지 않을 거라는 믿음을 기반으로 한다.
하지만 OO를 제공한다고 주창한 언어들이
실제로 C언어에서 누렸던 완벽한 캡슐화를 약화시켜 온 것은 틀림 없다.
상속
OO가 더 나은 캡슐화를 제공하지는 못했지만 상속만큼은 OO언어가 확실히 제공했다?
하지만 상속이란 단순히 어떤 변수와 함수를 하나의 유효 범위로 묶어서 재정의 하는 일에 불과하다.
사실 OO 언어가 있기 훨씬 이전에도 C 프로그래머는 언어의 도움 없이 손수 이러한 방식으로 구현할 수 있었다.
하지만 이렇게 말하는 데는 어폐가 있다.
상속을 흉내내는 요령은 있었지만, 사실상 상속만큼 편리한 방식은 절대 아니기 때문이다.
게다가 이 방식을 이용해 다중 상속을 구현하기란 훨씬 더 어려운 일이었다.
따라서 OO언어가 완전히 새로운 개념을 만들지는 못했지만,
데이터 구조에 가면을 씌우는 일을 상당히 편리한 방식으로 제공했다고 볼 수는 있다.
간략히 요약하면, 캡슐화에 대해서는 OO에 점수를 줄 수 없고
상속에 대해서만 0.5점 정도 부여할 수 있다.
다형성
함수를 가리키는 포인터를 응용한 것이 다형성이라는 점
OO언어는 다형성을 제공하지는 못했지만,
다형성을 좀 더 안전하고 더욱 편리하게 사용할 수 있게 해준다.
함수에 대한 포인터를 직접 사용하여 다형적 행위를 만드는 이 방식에는 문제가 있는데,
함수포인터는 위험하다는 사실이다.
이런 기법은 프로그래머가 특정 관례를 수동으로 따르는 방식이다.
즉 이들은 포인터를 통해 모든 함수를 호출하는 관례를 지켜야 한다는 점도 기억해야 한다.
이 사실을 망각하게 되면 버그가 발생하고, 이건 찾아내고 없애기도 힘들다.
OO언어는 이런 관례를 없애주며, 실수할 위험이 없다.
OO언어는 과거 c프로그래머가 꿈에서야 볼 수 있던 강력한 능력을 제공한다.
이러한 이유로 OO는 제어흐름을 간접적으로 전환하는 규칙을 부과한다고 결론 지을 수 있다.
1950년대 후반에 우리는 프로그램이 장치 독립적이어야 한다는 사실을 배웠다.
당시 우리는 장치에 의존적인 수많은 프로그램을 만들고 나서야, 이들 프로그램이 다른 장치에서도 동일하게 동작할 수 있도록 만드는 것이 우리가 진정 바랐던 일임을 깨달았다.
천공카드에서 자기테이프로 넘어가며 수많은 코드를 재작성 해야 했는데,
플러그인 아키텍처는 이러한 입출력 장치 독립성을 지원하기 위해 만들어 졌고,
등장 이후 거의 모든 운영체제에서 구현되었따.
그럼에도 대다수 프로그래머는 이 방식을 확장해 적용하지 않았는데,
포인터를 사용하면 위험을 수반했기 때문이었다.
OO의 등장으로 언제 어디서든 플러그인 아키텍처를 적용할 수 있게 되었따.
의존성 역전
다형성을 안전하고 편리하게 적용할 수 있는 메커니즘이 등장하기 전 소프트웨어는
호출 트리에서 소스코드 의존성의 방향은 반드시 제어흐름을 따르게 된다.
OO언어가 다형성을 안전하고 편리하게 제공한다는 사실은 소스코드 의존성을 어디에서든 역전시킬 수 있다는 뜻이기도 하다.
호출하는 모듈이든 호출 받는 모듈이든 관계없이
소프트웨어 아키텍트는 소스코드 의존성을 원하는 방향으로 설정할 수 있다.
이것이 바로 OO가 제공하는 힘이다.
그리고 이것이 바로 OO가 지향하는 것이다.
(최소한 아키텍트의 관점에서는)
독립적으로 배포할 수 있는 배포독립성을 갖게 되고,
서로 다른 팀에서 각 모듈을 독립적으로 개발할 수 있다.
이것이 개발독립성이다.
OO란 무엇인가?
다양한 의견과 수많은 답변이 있지만
소프트웨어 아키텍트 관점에서 정답은 명백하다.
OO란, 다형성을 이용하여 전체 시스템의 모든 소스코드 의존성에 대한 절대적인 제어권한을 획득할 수 있는 능력이다.
OO를 사용하면 아키텍트는 플러그인 아키텍처를 구성할 수 있고,
이를 통해 고수준의 정책을 포함하는 모듈은 저수준의 세부사항을 포함하는 모듈에 대해 독립성을 보장할 수 있다.