IT Book Summary/Clean Architecture

02 벽돌부터 시작하기 : 프로그래밍 패러다임 - 2

laughcryrepeat 2021. 2. 6. 17:50

4장 구조적 프로그래밍

에츠허르 비버 데이크스트라 - 진공관 시대의 이론물리학자, 최초의 프로그래머

 


증명

데이크스트라는 증명 Proof 수학적인 원리를 적용하여 프로그래밍의 문제를 해결하려고 했다.

--> 유클리드 계층구조를 만드는 것.

--> 입증된 구조를 이용하고, 이들 구조를 코드와 결합시켜, 코드가 올바르다는 사실을 스스로 증명하게 되는 방식.

--> 순차 sequence, 분기 selection, 반복 iteration 이라는 세가지 구조만으로 표현할수 있다는 사실을 증명.

 

구조적 프로그래밍의 탄생

  • 모듈을 증명 가능하게 하는 바로 그 제어구조가
    모든 프로그램을 만들 수 있는 제어구조의 최소 집합과 동일하다는 사실.

해로운 성명서

데이크스트라가 견해 ""grto문 의 해로움"

이 글에서 세가지 제어구조에 대한 의견을 피력했고 오랜기간 많은 반발이 있었지만

컴퓨터 언어가 진화하면서 데이크스트라의 의견이 맞았다.

기능적 분해

구조적 프로그래밍으로 모듈을 증명가능한 작은단위로 재귀적으로 분해할수 있었고,

결국 기능적으로 분해 가능했다.

 

이를 토대로 7-80년대에 구조적 분석, 설계 기법이 유행하였고, 

대규모 시스템을 모듈과 컴포넌트로 나누고, 입증가능한 작은 기능으로 세분화 하였다.

엄밀한 증명은 없었다

하지만 모든 증명은 이루어지지 않았고

대부분의 프로그래머들은 세세하게 증명하는 고된작업에서 이득을 얻지 못한다.

과학이 구출하다

과학적 방법은 반증은 가능하나 증명은 불가능하다.

반례를 들 수 없는 서술이 있다면 목표에 부합할 만큼은 참이라고 본다.

 

  • 수학은 증명가능한 서술이 참임을 입증하는 원리
  • 과학은 증명가능한 서술이 거짓임을 입증하는 원리

테스트

"테스트느 버그가 있음을 보여줄 뿐, 버그가 없을을 보여줄 수 없다."

 

소프트웨어는 수학보다는 오히려 과학과 같다.

거짓임을 증명하려는 테스트가 실패한다면, 이 기능들은 목표에 부합할 만큼은 충분히 참 이라고 여기게 된다.

 

결론

구조적 프로그래밍의 가치

-> 반증가능한 단위를 만들어 낼 수 있는 능력.

 

소프트웨서는 모든 수준에서 과학과 같고, 따라서 반증가능성에 의해 주도됨.

소프트웨어 아키텍트는 모듈, 컴포넌트, 서비스사 쉽게 반증 가능하도록(테스트 쉽도록) 만들어야함.

 

 


5장 객체지향 프로그래밍

좋은 아키텍처를 만드는 일은 객체지향 Object-Oriented  ** 설걔원칙을 이해하고 응용하는데서 출발함.

 


캡슐화?

데이터와 함수가 응집력있게 구성된 집단을 서로 구분 짓는 선을 그을 수 있다.

구분선 바깥에서 데이터는 은익되고, 일부 함수만 외부에 노출

 

 **프로그래밍은 프로그래머가 캡슐화된 데이터를 우회해서 사용하지 않을거라는 믿음을 기반으로 한다.

하지만 완벽한 캡슐화가 약화되어진건 맞다.

 

상속?

**는 상속은 확실히 제공한다.

** 언어가 고안되기 이전부터 상속과 비슷한 기법이 사용되었지만, 다중상속까지 구현하기는 어려웠다.

 

다형성?

c에서는 함수를 가리키는 포인터를 응용한 것이 다형성이라는 점.

1940년대  후반 폰 노이만 아키텍처가 처음 구현된 이후 프로그래머는 다형적 행위를 수행하기 위해 

함수를 가리키는 포인터를 사용해 옴.

하지만 이들 포인터를 초기화 하는 관례를 준수해야 하므로 함수포인터는 위험할 수 있다.

 

** 언어는 이런 관례를 없애주고 실수할 위험이 없다.

 

다형성이 가진 힘

**의 등장으로 언제나 플러그인 아키텍처를 적용할 수 있게 되었다

 

의존성 역전

호출트리에서 소스코드 의존성의 방향은 반드시 제어흐름 flow of control을 따르게 됨.

제어흐름은 시스템의 행위에 따라 결정되며, 소스코드 의존성은 제어흐름에 따라 결정.

 

의존성 역전

HL1 모듈은 ML1 모듈의 F() 함수를 호출한다.

HL1 모듈은 인터페이스를 통해 F() 함수를 호출한다.

이 인터페이스는 런타임에는 존재하지 않고, 단순히 ML1의 F() 함수를 호출하는것.

하지만 ML1과 I 인터페이스 사이의 소스코드 의존성(상속)이 제어흐름과 반대이다.

이는 의존성 역전 dependency inversion 이라고 부르며 아키텍트관점에서 중요한 의미를 가진다.

 

즉, 호출하는 모듈이든 받는 모듈이든  소프트웨어 아키텍트는 소스코드 의존성을 원하는 방향으로 설정가능.

이것은 컴포넌트들을 개별적이며 독립적으로 배포 가능(배포 독립성 independent deployability) 하게 만들 수 있으며,

 각 모듈을 독립적으로 개발할 수 있는 개발 독립성(independent developability) 도 가지게 된다.

결론

** 란 다형성을 이용해 전체 시스템의 모든 소스코드 의존성에 대한 절대적 제어권한을 획득할 수 있는 능력.

**를 사용하면 플러그인 아키텍처르 구성할 수 있고, 

고수준 정책을 포함하는 모듈은 저수준세부사항을 포함하는 모듈에 대해 독립성 보장한다.

저수준 세부사항은 중요도가 낮은 플러그인 모듈로 만들 수 있고, 독립적으로 개발 배포 가능하다.

 


6장 함수형 프로그래밍

핵심이 되는 기반은 람다 lamda 계산법으로 알론소 처치가 1930년대에 발명.


정수를 제곱하기

25까지 정수의 제곱을 출력하는 간단한 문제

 

리스프에서 파생한 함수형 언어 클로저 Clojure 

(println (take 25 (map (fn [x] (* x x)) (range) )))

(println ;___ 출력한다.
  (take 25 ;___ 처음부터 25까지
    (map (fn [x] (* x x)) ;___ 제곱을
      (range) ))) ;___ 정수의

리스프에서는 함수를 괄호안에 넣는 방식으로 호출

  • range 함수는 0부터 시작해 끝이 없는 정수 리스트를 반환
  • 반환된 정수 리스트를 map함수로 전달, 각 정수에 대해 제곱을 계산하는 익명함수를 호출,
    모든 정수의 제곱에 대해 끝이 없는 리스트를 생성. (실제로 접근하기 전까지 평가가 이뤄지지 않음.)
  • 제곱된 리스트는 take 함수로 전달, 이 함수느 앞의 25개까지 항목으로 구성된 새로운 리스트를 반환.
  • println 함수는 입력값을 출력하는데, 이 경우 입력은 앞의 25개의 정수에 대한 제곱값으로 구성된 리스트.

클로저 프로그램에서는 가변변수가 없음. x와 같은 변수가 한번 초기화되면 절대 변하지 않는다.

함수형 언어에서 변수는 변경되지 않는다

 

불변성과 아키텍처

아키테트느 왜 변수의 가변성을 염려하는가?

-> 경합 race 조건, 교착상태 deadlock 조건, 동시 업데이트 concurrent update 문제가 모두 가변변수로 발생.

-> 다수의 스레드와 프로세스를 사용하는 애플리케이션에서 맞치는 모든 문제

 

가변성의 분리

애플리케이션 내부의 서비스를 가변 커포넌트와 불변 컴포넌트로 분리하는 일.

 

상태 변경과 트랜잭션 메모리

불변 컴포넌트는 함수형 방식으로만 작업 처리, 

그리고 하나 이상의 가변 컴포넌트와 서로 통신.

 

상태변경은 컴포넌트를 갖가지 동시성 문제에 노출하므로,

트랜잭션 메모리를 사용해 동시 업데이트와 경합조건 문제로부터 가변변수를 보호한다.

 

가변 컴포넌트와 불변 컴포넌트르 분리하고 가변변수들을 보호하는 적절한 수단을 동원해야한다.

아키텍트는 가능한 많은 처리를 불변 컴포넌트로 옮기고, 가변 컴포넌트에서는 많은 코드를 빼야한다.

이벤트 소싱

상태가 아닌 트랜잭션을 저장하는 전략.

상태가 필요하면 단순 상태의 시작점부터 모든 트랜잭션을 처리한다.

 

데이터 저장소에 변경, 삭제가 발생하지 않고 CRUD 가 아닌 CR만 수행하면,

저장공간과 처리 능력이 충분한 애플리케이션이 완전한 불변성을 갖도록 완전한 함수형으로 만들 수 있다.

소스코드 버전관리 시스템이 이 방식으로 동작한다.

결론

  • 구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율이다.
  • 객체지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.
  • 함수형 프로그래밍은 변수 할당에 부과되는 규율이다.

각 패러다임은 코드를 작성하는 방식의 형태를 한정시킨다.

소프트웨어의 핵심은 여전히 그대로다.

-> 순차 sequence, 분기 selection, 반복 iteration, 참조 indirection