11장. 시스템
높은 추상화 수준(= 시스템 수준)에서도 깨끗함을 유지하는 방법
시스템 제작과 시스템 사용 분리하기
✅ SW시스템은 준비과정과 런타임 로직을 분리하라
✅ 시작단계를 분리하라
설정 논리는 일반 실행 논리와 분리해야 모듈성↑ 주요 의존성 해소를 위해 전반적이고 일관적인 방식 필요
- Main 분리: 생성과 관련한 코드는 모두 main 혹은 main이 호출하는 모듈로 이동
- 팩토리 사용: 객체 생성 시점을 애플리케이션이 통제해야할 경우 사용
- 의존성 주입: 한 객체가 맡은 보조 책임을 새로운 객체에게 전적으로 떠넘기는 **제어 역전 기법(Inversion of Control, IoC)**을 의존성 관리에 적용
확장
‘처음부터 올바른’ 시스템 제작은 어렵다. 대신 사용자 스토리의 변화에 따라 시스템을 조정하고 확장해나가야 한다.
SW 아키텍처의 경우 관심사를 적절히 분리하여 관리하면 점진적인 발전 및 확장이 가능하다.
- 횡단(cross-cutting) 관심사: 다른 관심사에 영향을 미치는 프로그램의 aspect
- 관점 지향 프로그래밍(Aspect-Oriented Programming, AOP): 횡단 관심사에 대처해 모듈성dmf 확보하는 일반적인 방법론
자바 프록시
개별 객체나 클래스에서 메서드 호출을 감싸는 경우와 같이 단순한 상황에 적합
JDK에서 제공하는 동적 프록시는 인터페이스만 지원 클래스 프록시 사용 원할 경우 바이트 코드 처리 라이브러리 필요
→ 프록시를 사용하면 깨끗한 코드 작성이 어려움! (= 코드의 양과 크기↑)
순수 자바 AOP 프레임워크
순수 자바를 구현하는 여러 자바 프레임워크는 내부적으로 프록시를 사용함
- 프로그래머는 설정 파일, API를 사용하여 (횡단 관심사를 포함한) 필수적인 앱 기반 구조 구현
- 프레임워크는 사용자 몰래 프록시나 바이트코드 라이브러리를 사용하여 구현
AspectJ 관점
관심사를 관점으로 분리하는 가장 강력한 도구 언어 차원에서 관점을 모듈화 구성으로 지원하는 자바 언어 확장
- 장점: 강력하고 풍부한 도구 집합 제공
- 단점: 새 도구 사용법 및 새 언어 문법을 익혀야 함
ex) 애너테이션 폼
테스트 주도 시스템 아키텍처 구축
애플리케이션 도메인 논리를 POJO로 작성 가능하다면, 완성도 높은 테스트 주도 아키텍처 구축 가능 ↔ BDUF
cf) BDUF(Big Design Up Front): a software development approach in which the program's design is to be completed and perfected before that program's implementation is started. (출처: wikipedia)
최선의 시스템 구조: 각기 POJO 객체로 구현되는 모듈화된 관심사 영역(도메인)으로 구성 → 서로 다른 영역은 해당 영역 코드에 최소한의 영향을 미치는 관점이나 유사한 도구를 사용하여 통합
의사 결정 최적화
가능한 마지막 순간까지 결정을 미루어 최대한 정보를 모아 최선의 결정을 내림
관심사를 모듈로 분리한 POJO 시스템으로부터 비롯된 기민함은 최신 정보에 기반하여 최선의 시점에 최적의 결정을 내릴 수 있도록 하고, 결정의 복잡성을 줄여줌
명백한 가치가 있을 때의 현명한 표준 사용
표준을 만드는 시간이 너무 길어지거나 표준을 제정한 목적에서 벗어나지 않도록 해야 함
도메인 특화 언어
DSL(Domain-Specific Language): 간단한 스크립트 언어나 표준 언어로 구현한 API
좋은 DSL는...
- 도메인 개념과 그 개념을 구현한 코드 사이의 의사소통 간극↓
- 추상화 수준을 코드 관용구나 디자인 패턴 이상으로 ↑
💡 즉, 시스템 역시 깨끗해야 한다! 모든 추상화 단계에서 의도는 명확히 표현해야 하기 위해서 POJO를 작성하고 관점 혹은 관점과 유사한 메커니즘을 사용해 관심사를 분리해야 한다!
12장. 창발성(創發性)
창발적 설계로 깔끔한 코드를 구현하자
켄트 백의 단순 설계 규칙
- 모든 테스트를 실행하라
테스트 케이스를 항상 통과하는 ‘테스트가 가능한 시스템’을 만들기 테스트 케이스 작성을 위해 결합도↓, 응집력↑ ⇒ 설계 품질↑ - 중복을 없애라
중복은 곧 추가 작업, 추가 위험, 불필요한 복잡도를 의미함
ex) 동일한 코드 삭제, 공통적인 코드를 새 메서드로 뽑기, TEMPLATE METHOD 패턴 사용하기 - 프로그래머의 의도를 표현하라
자신만 이해하는 것이 아니라 모두(특히, 유지보수 개발자)가 이해할 수 있도록 분명한 코드 작성하기- 좋은 이름 선택하기
- 함수와 클래스의 크기 줄이기
- 표준 명칭 사용하기
- 단위 테스트 케이스 작성하기
- 클래스와 메서드 수를 최소로 줄여라
우선순위는 가장 낮은 규칙
클래스와 메서드의 크기를 줄이기 위해 작은 클래스와 메서드를 많이 만드는 것X 클래스와 메서드의 크기를 작게 유지하면서 동시에 시스템의 크기도 작게 유지하기
13장. 동시성
동시성이 필요한 이유
동시성: 결합을 없애는 전략이자 무엇(what)과 언제(when)를 분리하는 전략 ⇒ 애플리케이션의 구조와 효율↑
- 대량의 정보를 처리해야하는 경우와 같이 반드시 동시성이 필요한 상황 존재
동시성은...
- 부하 유발
- 높은 복잡도
- 일반적으로 동시성 버그는 재현하기 어려움
- 근본적인 설계 전략 재고
동시성 방어 원칙
- 단일 책임 원칙(Single Responsibility Principle, SRP)
주어진 메서드/클래스/컴포넌트를 변경할 이유가 하나여야 한다는 원칙
즉, 동시성과 관련된 코드는 다른 코드와 분리해야 함
cf) 동시성 구현 시 고려해야 할 점
-
- 동시성 코드는 독자적인 개발, 변경, 조율 주기 있음
- 동시성 코드엔느 독자적인 난관(어려움)이 있음
- 잘못 구현한 동시성 코드는 다양한 방식을 실패함
- 따름 정리(Corollary)
- 자료 범위를 제한하라: 공유 객체를 사용하는 코드 내 임계영역을 synchronized 키워드로 보호 (= 자료의 캡슐화)
- 자료 사본을 사용하라: 공유 자료를 줄이기 위해 처음부터 공유를 하지 않는 방법
- 스레드는 가능한 독립적으로 구현하라: 다른 스레드와 자료 공유X
- 라이브러리를 이해하라: 언어가 제공하는 클래스 검토 → 적절한 클래스 사용법을 익히기
- 실행 모델을 이해하라
- 생산자-소비자(Producer-Consumer)
- 읽기-쓰기(Readers-Writers)
- 식사하는 철학자들(Dining Philosophers)
- 다음의 각각의 알고리즘과 해법 익히기
- 동기화하는 메서드 사이의 의존성을 이해하라
- 공유 객체 하나에는 메서드 하나만 사용하기
- 여러 메서드가 필요할 경우: 클라이언트에서 잠금/서버에서 잠금/연결 서버 중 하나 고려하기
- 동기화하는 부분을 작기 만들어라
- 올바른 종료 코드를 고민하라: 이미 나온 알고리즘을 통해 올바른 종료 코드 구현하기
- 스레드 코드를 테스트 하라: 문제를 노출하는 테스트 케이스 작성하기
- 그 외 고려사항
- 말이 안 되는 실패는 잠정적인 스레드 문제로 취급하기
- 다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들기
- 다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있도록 스레드 코드 구현하기
- 다중 스레드를 쓰는 코드 부분을 상화엥 맞춰 조정할 수 있도록 작성하기
- 프로세서 수보다 많은 스레드 돌려보기
- 다른 플랫폼에서 돌려보기
- 코드에 보조 코드를 넣어서 돌려보기. 강제로 실패하게 해보기
💡 다중 스레드 코드는 올바르게 구현하기 어렵다!
따라서 다중 스레드 코드를 작성할 때는 특별히 더 깨끗한 코드를 작성하도록 해야한다.
'3-2기 스터디 > 클린코드 독서' 카테고리의 다른 글
[6주차] 15~16장 정리 (0) | 2022.05.22 |
---|---|
[5주차] 14장 정리 (0) | 2022.05.17 |
[3주차] 클린코드 7~10장 정리 (0) | 2022.04.15 |
[2주차] 클린코드 4~6장 정리 (0) | 2022.04.06 |
[1주차] 클린코드 1~3장 정리 (0) | 2022.04.06 |
댓글