- 소프트웨어 아키텍처는 코드로부터 시작한다.
- 프로그래밍 언어는 쉴 틈 없이 쏟아져나왔으며, 프로그래밍 패러다임도 몰아쳤다.
- 패러다임: 프로그래밍을 하는 방법으로 대체로 언어에 독립적이다. 패러다임은 어떤 프로그래밍 구조를 사용할지, 그리고 언제 이 구조를 사용해야 하는지를 결정한다.
패러다임 개요
- 구조적 프로그래밍: 제어흐름의 직접적인 전환에 대해 규칙을 부과한다.
- 무분별한 점프는 프로그램 구조에 해롭다는 사실을 제시했다.
- 객체 지향 프로그래밍: 제어흐름의 간접적인 전황에 대해 규칙을 부과한다.
- 함수 호출 스택 프레임을 힙으로 옮기면, 함수 호출이 반환된 이후에도 함수에서 선언된 지역변수가 오랫동안 유지될 수 있음을 발견했다.
- 함수형 프로그래밍: 할당문에 대해 규칙을 부과한다.
- 람다 계싼법을 사용한다. 람다 계산법의 기초가 되는 개념은 불변성과 심볼의 값이 변경되지 않는다는 개념이다.
- 함수형 언어네는 할당문이 전혀없다.
- 각 패러다임은 무엇을 해야 할지를 말하기보다는 무엇을 해서는 안 되는지를 알려준다.
- 각 패러다임이 우리에게서 무언가를 빼앗는다.
구조적 프로그래밍
데이크스트라는 수학자가 유클리드 계층구조를 사용하는 방식을 프로그래머도 사용할 수 있다고 믿었따. 픅로그래머는 입증된 구조를 이용하고, 이들 구조를 코드와 결합시켜 코드가 올바르다는 사실을 스스로 증명하게 되는 방식이었다.
데이크스트라는 연구를 진행하면서 goto 문장이 분할 정복 접근법을 방해하는 경우가 있다는 사실을 발견했다.
뵘과 야코피니는 모든 프로그램을 순차, 분기, 반복이라는 세가지 구조만으로 표현할 수 있다는 사실을 증명했다.
여러 논쟁 끝에 goto 문장은 계속 뒤편으로 밀려났고, 마침내 거의 사라졌다.
구조적 프로그래밍을 통해 모듈을 증명 가능한 더 작은 단위로 재귀적으로 분해할 수 있게 되었고, 이는 결국 모듈을 기능적으로 분해할 수 있음을 뜻했다.
테스트는 버그가 있음을 보여줄 뿐, 버그가 없음을 보여줄 수는 없다.
구조적 프로그래밍은 프로그램을 증명 가능한 세부 기능 집합으로 재귀적으로 분해할 것을 강요한다.
- 그리고 나서 테스트를 통해 증명 가능한 세부 기능들이 거짓인지 증명하려고 시도한다.
- 거짓임을 증명하려는 테스트가 실패한다면, 이 기능들은 목표에 부합할 만큼 충분히 참이라고 여기게 된다.
구조적 프로그래밍이 오늘날까지 가치 있는 이유는 프로그래밍에서 반증 가능한 단위를 만들어 낼 수 있는 능력때문이다.
객체 지향 프로그래밍
- OO의 본질을 설명하기 위한 세 가지 부류
- 캡슐화
- 상속
- 다형성
- 데이터와 함수를 쉽고 효과적으로 캡슐화하는 방법을 OO 언어가 제공하고 있다.
- 이를 통해 구분선 바깥에서 데이터는 은닉되고, 일부 함수만이 외부에 노출된다.
- 하지만 실제 많은 OO 언어가 헤더와 구현체를 분리하는 방법을 모두 버렸고, 캡슐화를 거의 강제하지 않는다.
- OO 프로그래밍은 프로그래머가 충분히 올바르게 행동함으로써 캡슐화된 데이터를 우회해서 사용하지 않을거라는 믿음을 기반으로 한다.
- 상속은 단순히 어떤 변수와 함수를 하나의 유효 범위로 묶어서 재정의하는 일에 불과하다.
- OO 언어가 있기 전에도 함수 포인터를 이용해서 다형성을 표현할 수 있었다.
- OO 언어는 다형성을 좀 더 안전하고 더욱 편리하게 사용할 수 있게 해준다.
- 다형성으로 플러그인 아키텍처가 가능해졌다. 플러그인 아키텍처는 입출력 장치 독립성을 지원하기 위해 만들어졌고, 등장 이후 거의 모든 운영체제에서 구현되었다.
- 다형성으로 의존성 역전이 가능해졌다. 의존성 역전으로 아키텍트는 시스템의 소스 코드 의존성 전부에 대해 방향을 결정할 수 이쓴 절대적인 권한을 갖는다. 의존성 역전으로 배포 독립성과 개발 독립성을 갖는다.
- OO란 다형성을 이용하여 전체 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있는 능력이다.
함수형 프로그래밍
- 함수형 언어에서는 변수는 변경되지 않는다.
- 아키텍처를 고려할 때 불변성이 중요한 이유: 경합 조건, 교착 상태, 동시 업데이트 문제가 모두 가변 변수로 인해 발생하기 때문이다.
- 저장 공간이 무한하고 프로세서의 속도가 무한히 빠르다는 전제하에서는 불변성이 실형 가능하지만, 실제에서는 일종의 타협이 필요하다.
- 가장 중요한 타협 중 하나는 애플리케이션, 또는 애플리케이션 내부의 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리하는 일이다. 이렇게 분리하고 가변 변수들을 보호하는 적절한 수단을 동원해 뒷받침해야 한다.
- 현명한 아키텍트라면 가능한 한 많은 처리를 불변 컴포넌트로 옮겨야 하고, 가변 컴포넌트에서는 가능한 한 많은 코드를 빼내야 한다.
- 저장 공간과 처리 능력의 한계가 급격히 사라지고 있어서 이벤트 소싱이 가능해졌다.
- 이벤트 소싱은 상태가 아닌 트랜잭션을 저장하는 전략이다.
- 데이터 저장소에서 변경과 삭제가 전혀 발생하지 않으므로 동시 업데이트 문제 또한 일어나지 않는다.