클린 코드 12 - 13장

Park Jae Hong·2023년 7월 10일
0

창발성

: 단순한 설계 규칙 네 가지가 소프트웨어 설계 품질을 높여준다.

  • 모든 테스트를 실행한다.

  • 중복을 없앤다.

  • 프로그래머 의도를 표현한다.

  • 클래스와 메서드 수를 최소로 줄인다.

단순한 설계 규칙 1: 모든 테스트를 실행하라

  • 무엇보다 먼저, 설계는 의도한 대로 돌아가는 시스템을 내놓아야 한다.

  • 검증이 불가능한 시스템은 절대 출시하면 안된다.

  • 다행스럽게도, 테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 더불어 높아진다.

크기가 작고 목적 하나만 수행하는 클래스가 나온다. SRP를 준수하는 클래스는 테스트가 훨씬 더 쉽다. 테스트 케이스가 많을 수록 개발자는 테스트가 쉽게 코드를 작성한다. 따라서 철저한 테스트가 가능한 시스템을 만들면 더 나은 설계가 얻어진다.

단순한 설계 규칙 2-4: 리펙터링

  • 코드를 점진적으로 리팩터링 해나간다.

  • 새로 추가하는 코드가 설계 품질을 낮추는가?
    → 그렇다면 깔끔히 정리한 후 테스트 케이스를 돌려 기존 기능을 깨뜨리지 않았다는 사실을 확인한다.

코드를 정리하면서 시스템이 깨질까 걱정할 필요가 없다.

중복을 없애라

: 우수한 설계에서 중복은 커다란 적이다. 중복은 추가 작업, 추가 위험, 불필요한 복잡도를 뜻하기 떄문이다. 중복은 여러 가지 형태로 표출된다. 똑같은 코드는 당연히 중복이다. 비슷한 코드는 더 비슷하게 코려주면 리팩터링이 쉬워진다.

표현하라

: 아마 우리 대다수는 엉망인 코드를 접한 경험이 있으리라. 아마 우리 대다수는 스스로 엉망인 코드를 내놓은 경험도 있으리라 자신이 이해하는 코드를 짜기는 쉽다. 코드를 짜는 사람만틈이나 문제를 깊이 이해할 가능성은 희박하다.

클래스와 메서드 수를 최소로 줄여라

: 중복을 제거하고, 의도를 표현하고, SRP를 준수한다는 기본적인 개념도 극단으로 치달으면 득보다 실이 많아진다. 클래스와 메서드 크기를 줄이자고 조금만 클래스와 메서드를 수없이 만드는 사례도 없지 않다. 이 규칙은 함수와 클래스 수를 가능한 줄이라고 제안한다.


동시성

동시성이 필요한 이유 ?

: 동시성은 결합을 없애는 전략이다. 즉, 무엇(What)과 언제(When)를 분리하는 전략이다. 스레드가 하나인 프로그램은 무엇과 언제가 서로 밀접하다. 그래서 호출 스택을 살펴보면 프로그램 상태가 곧 드러난다. 흔히 단일 스레드 프로그램을 디버깅하는 프로그래머는 일련의 정지점을 정한 후 어느 정지점에 걸렸는지 살펴보면서 시스템 상태를 파악한다.
무엇과 언제를 분리하면 애플리케이션 구조와 효율이 극적을 나아진다. 구조적인 관점에서 프로그램은 거대한 루프 하나가 아니라 작은 협력 프로그램 여럿으로 보인다. 따라서 시스템을 이해하기가 쉽고 문제를 분리하기도 쉽다.

동시성 장단점

장점

  • 동시성을 항상 성능을 높여준다.
    : 동시성은 때로 성능을 높여준다. 대기 시간이 아주 길어 여러 스테드가 프로세서를 공유할 수 있거나, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다. 어느 쪽도 일상적을 발생하는 상황은 아니다.

  • 동시성을 구현해도 설계는 변하지 않는다.
    : 단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다. 일반적으로 무엇과 언제를 분리하면 시스템 구조가 크게 달라진다.

  • 웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다.
    : 실제로 컨테이너가 어떻게 동작하는지, 어떻게 동시 수정, 데드락 등과 같은 문제를 피할 수 있는지를 알아야만 한다.

단점

  • 동시성은 다소 부하를 유발한다. 성능 측면에서 부하가 걸리며, 코드도 더 짜야한다.

  • 동시성은 복잡하다. 간단한 문제라도 동시성은 복잡하다.

  • 일반적을 동시성 버그는 재현하기 어렵다. 그래서 진짜 결함으로 간주되지 않고 일회성 문제로 여겨 무시하기 쉽다.

  • 동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야한다.

동시성 방어 원칙

  • 단일 책임 원칙
    : SRP 는 주어진 메서드/클래스/컴포넌트 를 변경할 이유가 하나여야 한다는 원칙이다. 동시성은 복잡성 하나만으로도 따로 분리할 이유가 충분하다. 즉 동시성 관련 코드는 다른 코드와 분리해야 한다는 뜻이다.

  • 따름 정리: 자료 범위를 제한하라
    : 앞서 봤듯이, 객체 하나를 공유한 후 동일 필드를 수정하던 두 스레드가 서로 간섭하므로 예상치 못한 결과를 내놓는다. 이런 문제를 해결하는 방안으로 공유 객체를 사용하는 코드 내 임계영역을 synchronized 키워드로 보호하라고 권장한다 (캡술화해서 공유 자원을 최소화하라)

  • 따름 정리: 자료 사본을 사용하라
    : 공유 자원을 줄이려면 처음부터 공유하지 않는 방법이 제일 좋다. 어떤 경우에는 객체를 복사해 읽기 전용으로 사용하는 방법이 가능하다 .어떤 경우에는 각 스레드가 객체를 복사해 사용한 후 스레드가 해당 사본에서 결과를 가져오는 방법도 가능하다.

  • 따름 정리: 스레드는 가능한 독립적으로 구현하라
    : 자신만의 세상에 존재하는 스레드를 구현한다. 즉 다른 스레드와 자료를 공유하지 않는다. 각 스레드는 클라이언트 요청 하나를 처리한다. 모든 정보는 비공유 출러에서 가져오며 로컬 변수에 저장한다. 그러면 각 스레드는 세상에 자신만 있는 듯이 돌아갈 수 있다. 다른 스레드와 동기화할 필요가 없다.

동기화하는 메서드 사이에 존재하는 의존성을 이해하라

: 동기화하는 메서드 사이에 의존성이 존재하면 동시성 코드에 찾아내기 어려운 버그가 생긴다. 자바 언어는 개별 메서드를 보호하는 synchronized 라는 개념을 지원하다.

동기화 하는 부분을 작게 만들어라

: 자바에서 synchronized 키워드를 사용하면 락을 설정한다. 같은 락으로 감싼 모든 코드 영역은 한 번에 한 스레드만 실행이 가능하다. 락은 스레드를 지연시키고 부하를 가중시킨다. 그러므로 여기저기서 synchronized 문으로 남발하는 코드는 바람직하지 않다.

  • 뮤텍스: 스레드 자체에 공유자원에 대한 소유권을 부여하는 방식
  • 세마포어: 접근할 수 있는 스레드 수를 제한하는 것

올바른 코드는 구현하기 어렵다.

: 영구적으로 돌아가는 시스템을 구현하는 방법과 잠시 둘다 깔끔하게 종료하는 시스템을 구현하는 방법은 다르다. 깔끔하게 종료하는 코드는 올바르게 구현하기 어렵다. 가장 흔히 발생하는 문제가 데드락이다. 즉, 스레드가 절대 오지 않을 시그널을 기다린다.

스레드 코드 테스트하기

: 코드가 올바르다고 증명하기는 현실적을 불가능하다. 테스트가 정확성을 보장하지는 않는다. 그럼에도 충분한 테스트는 위럼을 낮춘다. 스레드가 하나인 프로그램은 지금까지 한 말이 모두 옳다.

  • 말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라

  • 다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자

  • 다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있게 스레드 코드를 구현하라

  • 다중 스레드를 쓰는 코드 부분을 상황에 맞게 조율할 수 있게 작성하라

  • 프로세서 수보다 많은 스레드를 돌려보라

  • 다른 플랫폼에서 돌려보라

  • 코드에 보조 코드를 넣러 돌려라, 강제로 실패를 일으키게 해보라

  • 직접 구현하기
    : 코드에다 직접 wait(), sleep(), yield(), priority() 함수를 추가한다. 특별히 까다로운 테스트할 때 적합하다.

결론

: 다중 스레드 코드는 올바로 구현하기 어렵다. 간단했던 코드가 여러 스레드와 공유 자원을 추가하면서 악몽으로 변한다. 다중 스레드 코드를 작성한다면 각별히 깨끗하게 코드를 짜야한다. 주의하지 않으면 희귀하고 오묘한 오류에 직면하게 된다.

profile
The people who are crazy enough to think they can change the world are the ones who do. -Steve Jobs-

0개의 댓글