객체 지향 프로그래밍
- 1966년 등장
- 알골(ALGOL)언어의 함수 호출 스택 프레임(stack frame)을 힙(heap)으로 옮기면, 함수 호출이 반환된 이후에도 함수에서 선언된 지역 변수가 오랫동안 유지될 수 있음을 발견
- 이러한 함수가 클래스의 생성자, 지역 변수는 인스턴스 변수, 중첩 함수는 메소드가 되었음
객체 지향(Object-Oriented)란 무엇일까?
데이터와 함수의 조합?
- 그다지 만족스러운 대답은 아님, OOP 등장 이전에도 프로그래머는 데이터 구조를 함수에 전달해 왔다.
실제 세계를 모델링하는 새로운 방법?
- "실제 세계를 모델링한다"라는 말은 무엇을 의미하는 걸까? 왜 우리는 그 방향을 추구해야하는가?
- OO는 현실 세계와 의미적으로 가깝기 때문에 OO를 사용하면 소프트웨어를 좀 더 쉽게 이해할 수 있다.
- 하지만 의도가 불분명하며 정의가 모호하다.
캡슐화, 상속, 다형성 제공?
- 객체 지향 본질을 설명하기 위해 세 가지 특징 요소에 기대는 부류
- 일단, 이해를 위해 밑에서 이 세가지 개념을 간략히 살펴보자
캡슐화(encapsulation)
- 데이터와 함수를 쉽고 효과적으로 캡슐화하는 방법을 객체지향 언어가 제공해 준다.
- 이를 통해서 데이터와 함수가
응집력
있게 집단을 서로 구분할 수 있는 기준이 될 수 있다.- 기준 밖에서 데이터는 은닉되고, 일부 함수만이 외부에 노출 된다. (private / public)
- 실제로 c언어에서 캡슐화를 더 완벽하게 구현할 수 있다. 객체 지향으로 넘어오면서 오히려 캡슐화가 약화됬다. 따라서 객체지향이 강력한 캡슐화에 의존한다는 정의는 받아들이기 힘듬
상속(inheritance)
- 객체 지향 언어가 더 나은 캡슐화를 제공하지는 못했지만 상속만큼은 확실히 제공하였다.
- 상속이란 단순히 어떤 변수와 함수를 하나의 유효 범위로 묶어서 재정의하는 일에 불과
- 사실상 C 프로그래머도 언어의 도움 없이 순수 이러한 방식으로 상속 비슷하게 구현이 가능. but, 흉내내는 요령은 있지만 편리한 방식은 아님
- NamedPoint인자를 Point 타입으로 강제로 변환해야함 (-> OOP에서는 업케스팅(upcasting)이 암묵적으로 이루어짐)
- 사실상 C 프로그래머도 언어의 도움 없이 순수 이러한 방식으로 상속 비슷하게 구현이 가능. but, 흉내내는 요령은 있지만 편리한 방식은 아님
- 객체 지향 언어가 완전히 새로운 개념을 만들지는 못했지만, 데이터 구조에 가면을 씌우는 일을 상당히 편리한 방식으로 제공했다고 볼 수는 있음
다형성(polymorphism)
- 함수를 가리키는 포인터를 응용한 것이 다형성이며, 프로그래머는 다형적 행위를 수행하기 위해 함수를 가리키는 포인터를 예전부터 사용해 왔다.
- 객체 지향이 새롭게 만든 것은 전혀 없지만 다형성을 좀 더
안전
하고 더욱편리
하게 사용할 수 있게 해준다.- 함수 포인터를 직접 사용하여 다형적 행위를 만드는 것은 위험함 (프로그래머가 특정 관례를 수동으로 따르는 방식 -> 버그 발생 가능성 높음)
- 객체 지향 언어는 이러한 관례를 없애주며, 따라서 실수할 위험이 없다. 객체 지향 언어를 사용하면 다형성은 대수롭지 않은 일이 된다.
다형성이 가진 힘
1950년대 후반에 프로그램이 장치 독립적(device independent)
이어야 한다는 사실을 배웠다.
당시 장치에 의존적인(dependent)수많은 프로그램을 만들고 나서야, 이 프로그램이 다른 장치에서도 동일하게 동작할 수 있도록 만드는 것이 진정 바랐던 일임을 깨닫게 된 것이다.
- 천공카드 더미에서 입력을 읽어서 새로운 천공카드 더미로 출력을 생성하는 프로그램 작성
- 시간이 흘러 천공 카드 대신 자기 테이프(magnetic tape)를 사용하기 시작
- 기존 프로그램에서 상당히 많은 부분을 재작성해야 했음
천공카드냐 테이프냐에 관계없이 동일한 프로그램을 사용할 수 있다면 매우 유용할 것이다.
입출력 인터페이스를 천공카드/테이프가 구현하게 된다면 입력을 읽어서 출력을 생성하는 프로그램
은 별도로 컴파일을 다시 할 필요없이 사용이 가능하다.
입출력 방식이 추가되면 해당 인터페이스를 구현만 하면 된다.
해당 인터페이스는 입력을 읽어서 출력을 생성하는 프로그램의 플러그인(plugin)이 된다.
객체지향의 등장으로 어디서든 플러그인 아키텍처를 적용할 수 있게 됬따.
의존성 역전(DI, Depedency Inversion)
다형성이 존재하기 전 소프트웨어는 어떤 모습이었을까
전형적인 호출 트리의 경우 main 함수가 고수준 함수를 호출하고, 고수준 함수
는 다시 중간 수준 함수
를 호출, 중간 수준 함수
는 다시 저수준 함수
를 호출한다. 이러한 호출 트리에서 소스 코드 의존성의 방향은 제어흐름(flow of controle)을 따르게 된다.
여기에 다형성을 추가하면 의존성의 방향을 제어흐름과 다르게 가져갈 수 있다.
- HL1 모듈은 ML1 모듈의 F()함수를 호출한다.
- 소스코드에서는 HL1 모듈은 인터페이스를 통해 F()함수를 호출한다.
- 이 인터페이스는 런타임에는 존재하지 않는다. HL1은 단순히 ML1모듈의 함수 F()를 호출할 뿐이다.
ML1과 I 인터페이스 사이의 소스 코드 의존성(상속 관계)이 제어흐름과는 반대인 점을 주목해야 한다.
- 고수준 모듈을 구현하려면 저수준 모듈을 사용해야 하는데, 반대로 저수준 모듈이 고수준 모듈에 의존하는 상황이 됨
- HL1은 ML1을 추상화한 I인터페이스에 의존할 뿐, ML1에 의존하지 않는다.
이를 의존성 역전(dependency inversion)이라고 부른다. 객체 지향 언어가 다형성을 안전하고 편리하게 제공한다는 사실은 소스 코드의 의존성을 어디에서든 역전시킬 수 있다
는 뜻이기도 하다.
위의 호출 트리를 보면 수 많은 소스 코드 의존성이 존재하는데, 소스 코드 사이에 인터페이스를 추가함으로써 방향을 역전시킬 수 있다.
아키텍트는 시스템의 소스 코드 의존성 전부에 대해 방향을 결정할 수 있는 절대적 권한
을 갖는다. 즉 소스 코드 의존성을 원하는 방향으로 설정할 수 있다.
이런 권한으로 어떤 것을 할 수 있는가?
- 업무 규칙이 데이터베이스, UI에 의존하는 대신에, 시스템의 소스 코드 의존성을 반대로 배치하여 데이터베이스와 UI가 업무 규칙에 의존하게 만들 수 있다.
- UI와 데이터베이스가 업무 규칙의 플러그인이 되게할 수 있다. 즉, 업무 규칙의 소스 코드에서 UI나 데이터베이스를 호출하지 않는다.
- 업무 규칙, UI, 데이터베이스는 세 가지로 분리된 컴포넌트 또는 배포 가능한 단위(ex. jar)로 컴파일할 수 있고, 이 배포 단위들의 의존성 역시 소스 코드 사이의 의존성과 같다.
- 따라서 업무 규칙은 UI, 데이터베이스와는 독립적으로 배포할 수 있다. 이들 컴포넌트는 서로 개별적이며 독립적 배포가 가능하다.
- 배포 독립성(independent deployabilility)
결론
객체 지향이란 무엇인가? 이라는 질문에 소프트웨어 아키텍처 관점에서의 정답은 명확하다.
- 객체지향이란 다형성을 이용하여 전체 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있는 능력이다.
- 아키텍트는 플러그인 아키텍처를 구성할 수 있고, 이를 통해 고수준의 정책을 포함하는 모듈은 저수준의 세부사항을 포함하는 모듈에 대해 독립성을 보장할 수 있다.
- 저수준의 세부사항은 중요도가 낮은 플러그인 모듈로 만들 수 있고, 고수준의 정책을 포함하는 모듈과는 독립적으로 개발하고 배포할 수 있다.
Reference
- 클린 아키텍처 : 소프트웨어 구조와 설계의 원칙
'프로그래밍 노트 > 아키텍처' 카테고리의 다른 글
[클린 아키텍처] 5장. 아키텍처 (0) | 2023.11.11 |
---|---|
[클린 아키텍처] 4장. 컴포넌트 원칙 (4) | 2023.11.09 |
[클린 아키텍처] 3장. 설계 원칙 - 좋은 아키텍처를 정의하는 원칙(SOLID) (1) | 2023.10.17 |
[클린 아키텍처] 행위, 아키텍처 두가지 가치에 대하여 (0) | 2023.08.28 |
[클린 아키텍처] 설계? 아키텍처? (0) | 2023.08.28 |