1장. 설계와 아키텍처란?
설계(design) : 저수준의 구조 또는 결정사항
아키텍처(architecture) : 고수준의 무언가
차이점 : 고수준에서 저수준으로 향하는 의사결정의 연속성만이 있을 뿐이다.
목표
필요한 시스템을 만들고 유지보수하는 데 투입되는 인력을 최소화하는 데 있다.
결론
빨리 가는 유일한 방법은 제대로 가는 것이다.
2장. 두 가지 가치에 대한 이야기
행위(behavior)
소프트웨어의 첫 번째 가치는 바로 행위다. 프로그래머는 이해관계자가 기능 명세서나 요구사항 문서를 구체화할 수 있도록 돕는다.
아키텍처
소프트웨어의 두 번째 가치는 소프트웨어(software)라는 단어와 관련이 있다.
소프트웨어는 변경하기 쉬워야 한다. 이해관계자가 기능에 대한 생각을 바꾸면, 이러한 변경사항을 간단하고 쉽게 적용할 수 있어야 한다. 이러한 변경사항을 적용하는 데 드는 어려움은 변경되는 범위(scope)에 비례해야 하며, 변경사항의 형태(shape)와는 관련이 없어야 한다.
아키텍처는 형태에 독립적이어야 하고, 그럴수록 더 실용적이다.
더 높은 가치
1. 긴급하고 중요한 행위
2. 긴급하지는 않지만 중요한 아키텍처
3. 긴급하지만 중요하지 않은 행위
아키텍처를 위해 투쟁하라
3장. 패러다임 개요
구조적 프로그래밍
제어흐름의 직접적인 전환에 대해 규칙을 부과한다.
객체 지향 프로그래밍
제어흐름의 간접적인 전환에 대해 규칙을 부과한다.
함수형 프로그래밍
할당문에 대해 규칙을 부과한다.
4장. 구조적 프로그래밍
증명(proof)
아주 작은 세부사항이라도 간과하면 프로그램이 동작하는 것처럼 보이더라도 결국엔 예상 외의 방식으로 실패하곤 했다.
모든 프로그램을 순차(sequence), 분기(selection), 반복(iteration)이라는 세 가지 구조만으로 표현할 수 있다는 사실을 증명했다.
해로운 성명서
goto : 제어흐름을 제약 없이 직접 전환할 수 있는 선택권 자체를 언어에서 제공하지 않기 때문이다.
기능적 분해
거대한 문제 기술서를 받더라도 문제를 고수준의 기능들로 분해할 수 있다. 그리고 이들 각 기능은 다시 저수준의 함수들로 분해할 수 있고, 이러한 분해 과정을 끝없이 반복할 수 있다. 게다가 이렇게 분해한 기능들은 구조적 프로그래밍의 제한된 제어 구조를 이용하여 표현할 수 있다.
엄밀한 증명은 없었다.
과학이 구출하다.
수학 : 증명 가능한 서술이 참임을 입증하는 원리
과학 : 증명 가능한 서술이 거짓임을 입증하는 원리
테스트
소프트웨어는 과학과 같다. 최선을 다하더라도 올바르지 않음을 증명하는 데 실패함으로써 올바름을 보여주기 때문이다.
결론
구조적 프로그래밍이 오늘날까지 가치 있는 이유는 프로그래밍에서 반증 가능한 단위를 만들어 낼 수 있는 능력 때문이다.
구조적 프로그래밍은 제어 흐름의 직접적인 전환에 부과되는 규율이다.
5장. 객체 지향 프로그래밍
캡슐화?
상속?
다형성?
다형성 : 행위가 타입에 의존하는 것
Plugin Architecture : 입출력 장치 독립성을 지원하기 위해 만들어졌고, 등장 이후 거의 모든 운영체제에서 구현되었다.
의존성 : 제어흐름은 시스템의 행위에 따라 결정되며, 소스 코드 의존성은 제어 흐름에 따라 결정된다.
의존성 역전 : 호출하는 모듈이든 아니면 호출 받는 모듈이든 관계없이 소프트웨어 아키텍트는 소스 코드 의존성을 원하는 방향으로 설정할 수 있다.
배포 독립성 : 특정 컴포넌트의 소스 코드가 변경되면, 해당 코드가 포함된 컴포넌트만 다시 배포하면 된다.
개발 독립성 : 시스템의 모듈을 독립적으로 배포할 수 있게 되면, 서로 다른 팀에서 각 모듈을 독립적으로 개발할 수 있다.
결론
다형성을 이용하면 의존성 역전이 되고 컴포넌트는 개별적이며 독립적으로 배포 가능하다.
객체 지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.
6장. 함수형 프로그래밍
정수를 제곱하기
함수형 언어에서 변수는 변경되지 않는다.
불변성과 아키텍처
경합(race) 조건, 교착상태(deadlock) 조건, 동시 업데이트(concurrent update) 문제가 모두 가변 변수로 인해 발생한다.
가변성의 분리
이벤트 소싱 (event sourcing)
상태가 아닌 트랜잭션을 저장하자는 전략
결론
함수형 프로그래밍은 변수 할당에 부과되는 규율이다.
7장. SRP: 단일 책임 원칙
하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다.
액터 : 해당 변경을 요청하는 한 명 이상의 사람들
모듈 : 소스 파일
원칙을 위반하는 징후 1: 우발적 중복
A클래스의 세 가지 메서드가 서로 매우 다른 세 명의 액터를 책임질 때
징후 2: 병합
두 명의 서로 다른 개발자가, 그리고 아마도 서로 다른 팀에 속했을 두 개발자가 A클래스를 체크아웃받은 후 변경사항을 적용할 때
해결책
데이터와 메서드를 분리하는 방식
결론
단일 책임 원칙은 메서드와 클래스 수준의 원칙이다.
8장. OCP: 개방-폐쇄 원칙
소프트웨어 개체(artifact)는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
사고 실험
책임을 분리하고, 두 책임 중 하나에서 변경이 발생하더라도 다른 하나는 변경되지 않도록 소스 코드 의존성도 확실히 조직화해야 한다.
처리 과정을 클래스 단위로 분할하고, 클래스는 컴포넌트 단위로 분리한다.
모든 컴포넌트 관계는 단방향으로 이루어진다.
A컴포넌트에서 발생한 변경으로부터 B컴포넌트를 보호하려면 반드시 A컴포넌트가 B컴포넌트에 의존해야 한다.
방향성 제어
정보 은닉
결론
시스템을 확장하기 쉬운 동시에 변경으로 인해 시스템이 너무 많은 영향을 받지 않도록 하려면 시스템을 컴포넌트 단위로 분리하고, 저수준 컴포넌트에서 발생한 변경으로부터 고수준 컴포넌트를 보호할 수 있는 형태의 의존성 계층구조가 만들어지도록 해야 한다.
9장. LSP: 리스코프 치환 원칙
상속을 사용하도록 가이드하기
정사각형/직사각형 문제
LSP와 아키텍처
LSP 위배 사례
아키텍트는 REST 서비스들의 인터페이스가 서로 치환 가능하지 않다는 사실을 처리하는 중요하고 복잡한 매커니즘을 추가해야 한다.
결론
LSP는 아키텍처 수준까지 확장할 수 있고, 반드시 확장해야만 한다.
10장. ISP: 인터페이스 분리 원칙
ISP와 언어
동적 타입 언어를 사용하면 정적 타입 언어를 사용할 때보다 유연하며 결합도가 낮은 시스템을 만들 수 있는 이유는 바로 이 때문이다.
ISP를 아키텍처가 아니라, 언어와 관련된 문제라고 할 수 있다.
ISP와 아키텍처
결론
불필요한 짐을 실은 무언가에 의존하면 예상치도 못한 문제에 빠진다는 사실이다.
11장. DIP: 의존성 역전 원칙
유연성이 극대화된 시스템 : 소스 코드 의존성이 추상(abstraction)에 의존하며 구체(concretion)에는 의존하지 않는 시스템
우리가 의존하지 않도록 피하고자 하는 것은 바로 변동성이 큰(volatile) 구체적인 요소다.
안정된 추상화
뛰어난 소프트웨어 설계자와 아키텍트라면 인터페이스의 변동성을 낮추기 위해 애쓴다.
안정된 소프트웨어 아키텍처 : 변동성이 큰 구현체에 의존하는 일은 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처
- 변동성이 큰 구체 클래스를 참조하지 말라. 추상 팩토리(Abstract Factory)를 사용하도록 강제한다.
- 변동성이 큰 구체 클래스로부터 파생하지 말라. 상속은 아주 신중하게 사용해야 한다.
- 구체 함수를 오버라이드 하지 말라. 추상 함수로 선언하고 구현체들에서 각자의 용도에 맞게 구현해야 한다.
팩토리
구체 컴포넌트
결론
의존성 규칙 : 의존성은 이 곡선을 경계로, 더 추상적인 엔티티가 있는 쪽으로만 향한다.
12장. 컴포넌트
컴포넌트 : 시스템의 구성요소. 배포할 수 있는 가장 작은 단위. jar, gem, dll
여러 컴포넌트를 서로 묶어서 .war 파일과 같은 단일 아카이브로 만들 수도 있다.
플러그인이나. exe 파일로 만들어서 독립적으로 배포할 수도 있다.
반드시 독립적으로 배포 및 개발 가능한 컴포넌트를 갖춰야 한다.
컴포넌트의 간략한 역사
origin구문 > 라이브러리 함수의 소스 코드를 애플리케이션 코드에 직접 포함시킨 단일 프로그램으로 컴파일 > 함수 라이브러리의 소스 코드를 애플리케이션 코드로부터 분리 > 애플리케이션을 두 개의 주소 세그먼트로 분리하여 함수 라이브러리 공간을 사이에 두고 오가며 동작하게 배치
재배치성
해결책 : 재배치가 가능한 바이너리. 지능적인 로더를 사용해서 메모리에 재배치할 수 있는 형태의 바이너리를 생성하도록 컴파일러를 수정
로더 : 재배치 코드가 자리할 위치 정보를 전달받음
재배치 코드 : 로드한 데이터에서 어느 부분을 수정해야 정해진 주소에 로드할 수 있는지를 알려주는 플래그(바이너리에서 참조하는 메모리의 시작 주소) 삽입
링킹 로더 : 외부 정의를 로드할 위치가 정해지기만 하면 로더가 외부 참조를 외부 정의에 링크시킬 수 있음
링커
머피의 법칙 : 컴파일하고 링크하는 데 사용 가능한 시간을 모두 소모할 때까지 프로그램은 커진다.
무어(moore) 등장으로 해결
컴포넌트 플러그인 아키텍처 :. jar 파일, dll, 공유 라이브러리를 기존 애플리케이션에 플러그인 형태로 배포하는 것
결론
소프트웨어 컴포넌트 : 런타임에 플러그인 형태로 결합할 수 있는 동적 링크 파일
13장. 컴포넌트 응집도
REP: 재사용/릴리스 등가 원칙
새로운 릴리스가 나온다는 소식을 접하면, 개발자는 새 릴리스의 변경 사항을 살펴보고 기존 버전을 계속 쓸지 여부를 결정하곤 한다.
하나의 컴포넌트로 묶인 클래스와 모듈은 반드시 함께 릴리스할 수 있어야 한다.
CCP: 공통 폐쇄 원칙
동일한 시점에 동일한 이유로 변경되는 것을 한데 묶어라. 서로 다른 시점에 다른 이유로 변경되는 것들은 서로 분리하라.
CRP: 공통 재사용 원칙
컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 말라.
CRP는 어떤 클래스를 한데 묶어도 되는지보다는, 어떤 클래스를 한데 묶어서는 안 되는지에 대해서 훨씬 더 많은 것을 이야기한다.
ISP와의 관계
ISP는 사용하지 않은 메서드가 있는 클래스에 의존하지 말라고 조언한다. CRP는 사용하지 않은 클래스를 가진 컴포넌트에 의존하지 말라고 조언한다.
컴포넌트 응집도에 대한 균형 다이어그램
<113p 사진>
결론
다이어그램의 균형점은 거의 항상 유동적이다. 즉, 두 힘을 현재 상황에 맞게 잘 분배했더라도, 내년이 되면 맞지 않을 수 있다. 결과적으로 시간이 흐름에 따라 프로젝트의 초점이 개발가능성에서 재사용성으로 바뀌고, 그에 따라 컴포넌트를 구성하는 방식도 조금씩 흐트러지고 또 진화한다.
14장. 컴포넌트 결합
ADP: 의존성 비순환 원칙
컴포넌트 의존성 그래프에 순환이 있어서는 안 된다.
숙취 증후군 : 많은 개발자가 동일한 소스 파일을 수정하는 환경에서 발생한다.
해결책 1 - 주 단위 빌드
해결책 2 - 순환 의존성 제거하기
개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것
컴포넌트 사이의 의존성 구조를 반드시 관리해야 한다.
컴포넌트는 정점에 해당하고, 의존성 관계는 방향이 있는 간선에 해당한다.
어느 컴포넌트에서 시작하더라도, 의존성 관계를 따라가면서 최초의 컴포넌트로 되돌아갈 수 없다는 사실이다. 이 구조에는 순환이 없다. 즉, 이 구조는 비순환 방향 그래프다.
시스템 전체를 릴리스해야 할 때가 오면 릴리스 절차는 상향식으로 진행된다.
순환이 컴포넌트 의존성 그래프에 미치는 영향
순환 끊기
흐트러짐
하향식(top-down) 설계
SDP: 안정된 의존성 원칙
안정성의 방향으로(더 안정된 쪽에) 의존하라
당신이 모듈을 만들 때는 변경하기 쉽도록 설계했지만, 이 모듈에 누군가가 의존성을 매달아 버리면 당신의 모듈도 변경하기 어려워진다.
안정성
안정성은 변화가 발생하는 빈도와는 직접적인 관련이 없다.
(126p 그림과 설명)
안정성 지표
Fan-in: 안으로 들어오는 의존성. 컴포넌트 내부의 클래스에 의존하는 컴포넌트 외부의 클래스 개수를 나타낸다.
Fan-out: 바깥으로 나가는 의존성
'두두의 삶 > 두두의 책' 카테고리의 다른 글
[소설][독후감] "연애의 행방" - 히가시노 게이고 (0) | 2022.10.22 |
---|---|
[소설][독후감] "아주 편안한 죽음" - 시몬 드 보부아르 (0) | 2022.10.22 |
[에세이][독후감] “인생은 실전이다 - 아주 작은 날갯짓의 시작” - 신영준, 주언규 (0) | 2022.10.17 |
[소설][독후감] "단테의 신곡" 단테 알리기에리 (0) | 2022.10.14 |
[경제/경영][독후감] "돈의 연금술" - 데이브 램지 (0) | 2022.10.13 |