⛳️ 9. 창발성
목차
- 모튼 테스트를 실행한다
- 중복을 없앤다
- 프로그래머의 의도를 표현한다
- 클래스와 메서드 수를 최소로 줄인다.
창발적 설계란?
- 창발성 (Emergence)
- 하위 계층에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출연하는 현상
- 작은 요소들의 상호작용의 반복이 전체 구조에 영향을 미친다
- ex. 개미의 집짓기
- 창발적 설계
- 단순한 4가지 단계를 반복하다보면 전체적으로 깨끗한 코드가 만들어진다.
1. 모든 테스트를 실행하라
- 설계를 의도한 대로 돌아가는 시스템을 내놓아야 한다
- 테스트를 철저히 거쳐 모든 테스트 케이스를 항상 통과하는 시스템 ==
테스트가 가능한 시스템
- 테스트가 불가능한 시스템 👉 검증 불가능 👉 절대 출시 ❌
- 설계 품질이 더불어 높아짐
- 테스트가 가능한 시스템을 만드려고 할수록 설계 품질 UP
- 크기가 작고 목적 하나만 수행하는 클래스
- 결합도 낮춤
- DIP와 같은 원칙 적용
- 의존성 주입
- 인터페이스, 추상화 등 도구 사용
- 객체 지향 방법론이 지향하는 목표 달성 가능 👉
낮은 결합도
&높은 응집력
2. 리팩터링
- 기존의 코드 최대한 재활용
- 테스트 케이스를 모두 작성했으면 코드 클래스 정리 가능
- 테스트 케이스가 있으면 리팩터링 과정에서 시스템이 깨질 걱정은 안 해도 된다.
- 중복을 없애라
- 중복 : 추가 작업, 추가 위험, 불필요한 복잡도
- 구현 중복도 중복이다
- 소규모 재사용은 시스템 복잡도를 줄여줌
TEMPLATE METHOD
: 알고리즘의 구조를 상위 클래스의 메서드에서 정의하고, 하위 클래스에서 알맞게 세부적으로 정의- 알고리즘에 일정한 단계가 있고 세부적인 단계마다 조금씩 구현이 다를 때 사용
- 고차원 중복 제거 목적으로 사용
int size;
boolean isEmpty() {}
// 👇
boolean isEmpty() {
return this.size()==0;
}
3. 표현하라
개발자의 의도를 분명히 표현하라
- 좋은 이름을 선택한다.
- 함수와 클래스 크기를 가능한 줄인다 - 작은 클래스와 함수는 이름도 짓기 쉽다
- 표준 명칭을 사용한다.
- 디자인 패턴을 사용하면 그 이름을 클래스에 넣어주자
- 단위 테스트 케이스를 꼼꼼히 작성한다.
- 더 읽기 쉽게 만들기 위해 노력하자.
결함이 줄어들고 유지보수 비용이 적게 든다
4. 클래스와 메서드 수를 최소로 줄여라
- 함수와 클래스 수를 가능한 줄이자
- 무의미하고 독단적인 정책은 지양하고, 실용적인 방식을 택하자
- 클래스 크기를 작게 유지하고, 시스템 크기를 작게 유지하자는 목표 - 가장 낮은 우선순위
- 여러가지 규칙에 극단적으로 심취해 클래스와 메서드를 무수히 만들지 말자
- 실용적 관점에서 타협하자
예) 의존성 역전 원칙
- 당장 필요하지 않다면 or 확장 유연성이 필요하지 않다면 👉 과한 설계가 될 수 있음
- 만약 변경이 발생하면? 👉 그때 확장하는 설계를 새로 하면 된다
- 거의 발생하지 않을 미래의 일에 투자하는 비용이 더 크다
⛳️ 10. 동시성
목차
- 동시성이 필요한 이유
- 여러 스레드를 돌리는 어려움
- 안전한 동시성 프로그래밍 규칙
동시성 프로그래밍이란?
출처 : https://kwahome.medium.com/concurrency-is-not-parallelism-a5451d1cde8d
- 어플리케이션을 효율적으로 실행하기 위해 멀티코어를 온전히 활용하도록 구현하는 방식
- 외부 서비스의 응답을 기다리면서 아무 일도 하지 않으면 CPU 사이클이 낭비된다 👉 낭비되는 자원을 줄이자
- 동시성 & 병렬성 구현
- 언어 레벨에서 하드웨어의 멀티코어를 적절하게 사용하도록 지원 - 동시성만 잘 신경써서 개발하면 된다
- 클라이언트가 아닌 어플리케이션 관점
- 내 어플리케이션의 효율성을 높여야 한다
- 내 어플리케이션이 돌아가는 머신의 환경이 효율적으로 돌아가야 한다.
1. 동시성이 필요한 이유
- 동시성 = 결합을 없애는 전략
- 무엇과 언제를 분리
- 애플리케이션 구조와 효율이 극적으로 나아진다
동시성 채택의 이유
- 구조적 개선
- 응답 시간, 작업 처리량 개선 요구사항
동시성에 대한 오해
- 동시성은 항상 성능을 높여준다 👉 동시성은 때로 성능을 높여준다
- 여러 스레드가 프로세서 공유 가능, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 높아진다
- Java ) Servlet
- 하나의 서블릿에 여러 thread가 접근 가능
- 멤버 변수들은 Not Thread Safe
- Thread Safe한 자료구조도 일부 상황에서는 안전하지 않을 수 있음
- 어떤 상황까지 Thread Safe한 지 잘 이해해야 함
- 하나의 서블릿에 여러 thread가 접근 가능
- 동시성을 구현해도 설계는 변하지 않는다 👉 동시성을 구현하면 설계를 바꿔야 한다.
- Web이나 EJB와 같은 컨테이너를 사용하면 동시성을 이해할 필요가 없다 👉 컨테이너를 사용해도 동시성을 이해해야 한다
- 어플리케이션이 컨테이너를 통해 멀티 쓰레드를 사용하는 것 - 컨테이너의 동작을 이해해야 함
- 동시수정, 데드락과 같은 문제를 피할 수 있는지를 알아야 한다
동시성에 대한 진실
- 동시성은 다소 부하를 유발한다
- 동시성은 복잡하다
- 동시성으로 인한 버그는 재현하기 어렵다
- 근본적인 설계 전략을 재고해야 한다
2. 여러 스레드를 돌리는 어려움
- 잠재적인 경로가 무수히 많다.
- 대다수 경로는 올바른 결과를 내놓고 일부가 잘못된 결과를 내놓는다
3. 안전한 동시성 프로그래밍 원칙
- 단일 책임 원칙
- 메서드/클래스/컴포넌트를 변경할 이유가 하나여야 한다
- 동시성 관련 코드는 다른 코드와 분리하라
- 동시성 코드는 독자적인 개발, 변경, 조율 주기가 있다
- 동시성 코드는 독자적인 난관이 있다. 다른 코드에서 겪는 난관보다 훨씬 어렵다.
- 잘못 구현한 동시성 코드는 별의별 방식으로 실패한다
- 동시성 관련 코드는 다른 코드와 분리하라
- 메서드/클래스/컴포넌트를 변경할 이유가 하나여야 한다
- 자료 범위를 제한하라
- 객체 하나를 공유하는 두 세르다 - 서로 간섭해서 예상치 못한 결과 발생
- 객체를 사용하는 코드 내 임계영역을
synchronized
키워드로 보호 - 동시 수정 문제로부터 피하기 위함
- 이런 임계영역의 수를 최소하
- 보호할 임계영역을 빼거나 모든 임계영역을 보호했는지 확인하는 것도 시간적 비용 발생
- 자료 사본을 사용하라
- 공유 자본을 줄이려면 최대한 공유하지 않는 게 좋다
- 객체를 복사해 읽기 전용으로 사용
- 각 스레드가 객체를 복사해 사용한 후 한 스레드가 해당 사본에서 결과를 가져온다
- 사본을 사용하는 방식으로 내부 잠금을 없애 수행시간을 절약하는 것이 사본 생성과 가비지 컬렉션에 드는 부하를 상쇄할 가능성이 크다
- thread는 가능한 독립적으로 구현한다
- 다른 스레드와 자료를 공유하지 않는다
- 각 thread는 클라이언트 요청 하나를 처리
- 모든 자료는 비공유 출처(request)에서 가져오며 로컬 변수에 저장
- 마치 독자적인 시스템에서 동작하는 것처럼 요청 처리
- 라이브러리를 이해하라
- 동기화하는 메서드 사이에 존재하는 의존성을 이해하라
- 공유 객체 하나에는 메서드 하나만 사용하라
- 클라이언트에서 잠금 - 클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다. 마지막 메서드를 호출할 때까지 잠금을 유지
- 서버에서 잠금 - 서버에다 "서버를 잠그고 모든 메서드를 호출한 후 잠금을 해제하는" 메서드를 구현한다. 클라이언트는 이 메서드를 호출한다.
- 연결 서버 (Adapter) - 잠금을 수행하는 중간 단계를 생성한다. "서버에서 잠금" 방식과 유사하지만 원래 서버는 변경하지 않는다.
- 동시성 코드를 테스트 해야 한다.
- 테스트를 했다고 올바르다고 증명하기는 불가능하다. 하지만 위험을 충분히 낮춘다
- 문제를 노출하는 테스트 케이스를 작성하라
- 프로그램의 설정과 시스템 설정과 부하를 바꿔가며 자주 돌려라
- 테스트가 실패하면 원인을 추적하라
- 다시 돌렸더니 통과한다는 이유로 그냥 넘어가면 안 된다.
- 코드에 보조 코드를 넣어 돌려라
- 드물게 발생하는 오류를 자주 발생하도록 보조 코드를 추가한다.
- 직접 구현 :
wait()
,sleep()
,yield()
,priority()
함수를 추가 - 도구 사용 :
Thread.JigglePoint.jiggle()
- 무작위로sleep()
,yield()
호출
- 동시성 코드를 실제 환경/테스트 환경에서 돌려본다
- 다양한 요청과 상황에 동시성 코드가 정상적으로 동작하는지 확인
- 배포하기 전에 테스트 환경에서 충분히 오랜시간 검증
- 동시성 코드를 배포한 후에 모니터링을 통해 문제가 발생하는지 지켜본다.
- 다양한 요청과 상황에 동시성 코드가 정상적으로 동작하는지 확인
- 테스트를 했다고 올바르다고 증명하기는 불가능하다. 하지만 위험을 충분히 낮춘다
'Group Study (2021-2022) > Clean Code' 카테고리의 다른 글
[클린코드 북리뷰 스터디] 8주차 - 냄새와 휴리스틱 (0) | 2022.07.16 |
---|---|
[클린코드 북리뷰 스터디] 7주차 - 점진적인 개선, Junit 들여다보기, SerialDate 리팩터링 (0) | 2022.07.14 |
[클린코드 북리뷰 스터디] 5주차 - 클래스와 시스템 (0) | 2022.06.26 |
[클린코드 북리뷰 스터디] 4주차 - 경계와 단위 테스트 (0) | 2022.06.20 |
[클린코드 북리뷰 스터디] 2주차 - 주석, 형식 맞추기 (0) | 2022.05.20 |