객체지향 프로그래밍

들어가며

좋은 아키텍처를 만드는 일은 객체 지향 설계 원칙을 이해하고 응용하는 데서 출발한다.

그렇다면 객체 지향이란 무엇인가?

  1. 데이터와 함수의 조합 이는 o.f()와 f(o)가 다르다는 의미를 내포하기 때문에 터무니없다.
  2. 실제 세계를 모델링 하는 새로운 방법 이는 현실 세계와 의미적으로 가깝다는 뜻을 내포하는 듯하지만, 매우 모호하고 얼버무리는 데 지나지 않는다.

객체 지향의 본질을 설명하기 위해 캡슐화, 상속, 다형성이라는 3가지 주문에 기대는 부류도 있다. 이들은 객체 지향이 3가지 개념을 적절히 조합한 것이니 객체 지향 언어는 최소한 3가지 요소를 반드시 지원해야 한다고 한다.

이번 장은 3가지 개념을 차례대로 살펴본다.

캡슐화?

  • 데이터와 함수를 쉽고 효과적으로 캡슐화하는 방법을 객체 지향 언어가 제공한다.
  • 데이터와 함수가 응집력 있게 구성된 집단을 서로 구분 짓는 선을 그을 수 있다.
  • 구분선 이 되고, 일부 함수만이 외부에 노출된다.
  • 객체 지향 언어에서는 각각 클래스의 private 멤버 데이터와 public 멤버 함수로 표현된다.

사실 캡슐화는 C언어에서도 사용되던 방식이었다. 오히려 더욱 강력한 캡슐화를 지원해 주었다. 오히려 객체 지향 언어에서는 캡슐화가 약화되었다.

실제로 많은 객체 지향 언어가 캡슐화를 거의 강제하지 않으며, 객체 지향 프로그래밍은 프로그래머가 충분히 올바르게 행동함으로써 캡슐화된 데이터를 우회해서 사용하지 않을 거라는 믿음을 기반으로 한다.

상속?

객체 지향 언어가 고안되기 훨씬 이전에도 상속을 흉내 내는 요령은 있었지만 사실상 상속만큼 편리한 방식은 절대 아니었고, 여기에 다중 상속까지 구현하기란 훨씬 더 어려운 일이었다.

따라서 객체 지향 언어가 완전히 새로운 개념을 만들지는 못했지만, 상속에 대해 상당히 편리한 방식을 제공했다고 볼 수 있다.

다형성?

객체 지향 언어가 있기 전에 다형성을 표현할 수 있는 언어는 있었다. 하지만 객체 지향 언어는 다형성을 제공하지는 못했지만, 다형성을 좀 더 안전하고 더욱 편리하게 사용할 수 있게 해준다.

이전 언어들은 다형성을 표현할 수 있었지만, 포인터를 통해 초기화하는 관례를 준수해야 했다. 만약 이를 지키지 않는다면 버그가 발생하고, 이러한 버그는 찾아내고 없애기도 지독히 어렵다. 객체 지향 언어는 이러한 관례를 없애주며, 따라서 실수할 위험이 없다.

다형성이 가진 힘

  • 플러그인 아키텍처(plugin architecture)는 입출력 장치의 독립성을 지원하기 위해 만들어졌고, 등장 이후 거의 모든 운영체제에서 구현되었다.
  • 객체 지향 언어의 등장으로 언제 어디서든 플러그인 아키텍처를 적용할 수 있게 되었다.

예를 들어 자바 로그 분석 프로그램이 있을 때 톰캣 로그도 분석해야 한다면 많은 수정이 필요하겠지만, 다형성이 있다면 어떤 로그냐에 관계없이 동일한 프로그램을 사용할 수 있다면 매우 유용할 것이다.

의존성 역전

다형성을 안전하고 편리하게 적용할 수 있는 메커니즘이 등장하기 전, 호출 트리에서 소프트웨어 코드의 의존성 방향은 반드시 제어흐름(flow of control)을 따랐다.

소스 코드 의존성 vs 제어 흐름

하지만 다형성이 끼어들면 특별한 일이 일어난다.

의존성 역전

그림에서 HL1 모듈은 ML1 모듈의 F() 함수를 호출한다. 소스 코드에서는 HL1 모듈은 인터페이스를 통해 F()함수를 호출한다.

하지만 ML1과 I 인터페이스 사이의 소스 코드 의존성(상속 관계)이 제어흐름과는 반대인 점을 주목하자. 이를 의존성 역전(dependency inversion)이라고 한다.

이러한 접근법을 사용한다면, 객체지향 언어로 개발된 시스템을 다루는 소프트웨어 아키텍트는 시스템의 소스 코드 의존성 전부에 대해 방향을 결정할 수 있는 절대적인 권한을 갖는다. 이것이 바로 객체지향이 제공하는 힘이다! 그리고 이것이 객체지향이 지향하는 것이다. 그러면 이 힘으로 무엇을 할 수 있을까?

예를 들어 업무 규칙(Business rule)이 데이터베이스와 사용자 인터페이스(UI)에 의존하는 대신, 시스템의 소스 코드 의존성을 반대로 배치하여 데이터베이스와 UI가 업무 규칙에 의존하게 만들 수 있다.

즉, UI와 데이터베이스가 업무 규칙의 플러그인이 된다는 것이다. 다시 말해 업무 규칙의 소스 코드에서는 UI나 데이터베이스를 호출하지 않는다. 따라서 업무 규칙을 UI와 데이터베이스와 독립적으로 배포할 수 있다. UI나 데이터베이스에서 발생한 변경사항은 업무 규칙에 일정 영향을 미치지 않는다.

즉, 이들 컴포넌트는 개별적이며 독립적인 배포가 가능하다. 다시 말해 특정 컴포넌트 소스 코드가 변경되면, 해당 코드가 포함된 컴포넌트만 다시 배포하면 된다. 이것이 바로 배포 독립성(independent deployability)이다.

시스템의 모듈을 독립적으로 배포할 수 있게 되면, 서로 다른 팀에서 각 모듈을 독립적으로 개발할 수 있다. 이것이 개발 독립성(independent developability)이다.

결론

객체 지향이란 무엇인가? 소프트웨어 아키텍트 관점에서 정답은 명백하다. 객체 지향이란 다형성을 이용하여 전체 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있는 능력이다.

  • 객체 지향을 사용하면 아키텍트는 플러그인 아키텍처를 구성할 수 있다.
  • 이를 통해 고수준 정책을 포함하는 모듈은 저수준의 세부사항을 포함하는 모듈에 대해 독립성을 보장할 수 있다.
  • 저수준의 세부사항은 중요도가 낮은 플러그인 모듈로 만들 수 있다.
  • 고수준의 정책을 포함하는 모듈과는 독립적으로 개발하고 배포할 수 있다.