본문 바로가기
Study/Java

[Clean-Code] Chapter. 11-13

by novxerim 2023. 6. 23.

Clean-Code_week4_yerimi11

시스템

관심사 분리를 하지 않고 런타임 로직과 객체 생성 로직이 섞이는 경우, 단일 책임 원칙(Single Responsibility Principle)이 깨질 수 있다.
image

좋은 설계를 위해서는 객체 생성과 관련된 로직은 별도의 팩토리나 생성자 등의 메서드로 분리하여 단일 책임을 갖는 별도의 클래스로 위임하는 것이 좋다.
이를 통해 코드의 응집도를 높이고 유지보수성을 향상시킬 수 있다.

체계적이고 탄탄한 시스템을 만들고 싶다면 모듈성을 깨서는 절대로 안 된다.
객체를 생성하거나 의존성을 연결할 때도 마찬가지다.
설정 논리는 일반 실행 논리와 분리해야 모듈성이 높아진다.
또한 주요 의존성을 해소하기 위한 방식, 즉 전반적이며 일관적인 방식도 필요하다.

  • main()에서 생성 분리: 시스템 생성과 시스템 사용을 분리하는 방법
  • 팩토리로 생성 분리: 객체가 생성되는 시점을 애플리케이션이 결정하는 방법 ex) 주문처리 시스템(추상 팩토리 패턴)
    ㄴ 추상 팩토리 패턴: 생성할 객체의 종류에 따라 다양한 구체적인 팩토리 클래스를 만들고, 클라이언트가 이러한 팩토리를 사용하여 객체를 생성할 수 있도록 한다.
  • 의존성 주입(DI:Dependency Injection): 제어 역전(Inversion of Control, IoC)기법을 의존성 관리에 적용한 메커니즘. 설정자(setter) 메서드나 생성자 인수 혹은 둘 다를 제공한다.
    ㄴ 제어 역전에서는 한 객체가 맡은 보조 책임을 새로운 객체에게 전적으로 떠넘기고, 새로운 객체를 넘겨받은 책임만 맡으므로 단일 책임 원칙(SRP)을 지키게 된다.
  • 테스트 주도 시스템 아키텍처 구축: 코드 수준에서 아키텍처 관심사를 분리할 수 있다면, 진정한 테스트 주도 아키텍처 구축이 가능해진다.

최선의 시스템 구조는 각기 POJO (또는 다른) 객체로 구현되는 모듈화된 관심사 영역(도메인)으로 구성된다.
이렇게 서로 다른 영역은 해당 영역 코드에 최소한의 영향을 미치는 관점이나 유사한 도구를 사용해 통합한다.
이런 구조 역시 코드와 마찬가지로 테스트 주도 기법을 적용할 수 있다.

모든 추상화 단계에서 의도는 명확히 표현해야 한다. 그러려면 POJO를 작성하고 관점 혹은 관점과 유사한 메커니즘을 사용해 각 구현 관심사를 분리해야 한다.


창발성

켄트 벡이 제시한 단순한 설계 규칙 4가지

  1. 모든 테스트를 실행한다.
  2. 중복을 없앤다. (리팩터링)
  3. 프로그래머의 의도를 표현한다.
  4. 클래스와 메서드 수를 최소로 줄인다.

TEMPLATE METHOD 패턴은 고차원 중복을 제거할 목적으로 자주 사용하는 기법이다. (앞에서 나왔음)

코드를 명백하게 짜야한다.

  • 좋은 이름을 선택한다.
  • 함수와 클래스 크기를 가능한 줄인다.
  • 표준 명칭을 사용한다.
  • 단위 테스트 케이스를 꼼꼼히 작성한다.

동시성

동시성은 결합(coupling)을 없애는 전략이며, 무엇언제를 분리하는 전략이다.
스레드가 하나인 프로그램은 무엇언제가 서로 밀접하다.

서블릿(Servlet) 모델: 웹 or EJB 컨테이너 아래서 작동, 이들 컨테이너는 동시성을 부분적으로 관리한다. 웹 요청이 들어올 때마다 웹 서버는 비동기식으로 서블릿을 실행한다.

[동시성에 대한 오해]

  • 동시성은 항상 성능을 높여준다.
    동시성은 때로 성능을 높여준다. 대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있거나, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다. 어느 쪽도 일상적으로 발생하는 상황은 아니다.
  • 동시성을 구현해도 설계는 변하지 않는다.
    단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다. 일반적으로 무엇과 언제를 분리하면 시스템 구조가 크게 달라진다.
  • 웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다.
    실제로는 컨테이너가 어떻게 동작하는지, 어떻게 동시 수정, 데드락 등과 같은 문제를 피할 수 있는지를 알아야만 한다.

[동시성과 관련된 타당한 생각]

  • 동시성은 다소 부하를 유발한다. 성능 측면에서 부하가 걸리며, 코드도 더 짜야한다.
  • 동시성은복잡하다. 간단한 문제라도 동시성은 복잡하다.
  • 일반적으로 동시성 버그는 재현하기 어렵다. 그래서 진짜 결함으로 간주되지 않고 일회성 문제로 여겨 무시하기 쉽다.
  • 동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야 한다.

[동시성 방어 원칙]

  • 단일 책임 원칙(Single Responsibility Principle, SRP), 동시성 코드는 다른 코드와 분리하라.
  • 공유 객체를 사용하는 코드 내 임계영역(critical section)을 synchronized 키워드로 보호 하라. 임계영역의 수를 줄여라.
  • 객체를 복사해 사용해라. 사본으로 동기화를 피할수 있다면 내부 잠금을 없애 절약한 수행 시간이 사본 생성과 가비지 컬렉 션에 드는 부하를 상쇄할 가능성이 크다.
  • 다른 스레드와 자료를 공유 하지 않는다. 각 스레드는 클라이언트 요청 하나를 처리한다. 모든 정보는 비공유 출처에서 가져오며 로컬 변수에 저장한다. 독자적인 스레드로. 가능하면 다른 프로세서에서 돌려도 괜찮도록 자료를 독립적인 단위로 분할하라.

(JAVA) 스레드 환경에 안전한 컬렉션: java.util.concurrent 패키지가 제공하는 클래스는 다중 스레드 환경에서 사용해도 안전하며, 성능도 좋다. ConcurrentHashMap은 HashMap보다 빠르기도 하다.

CS공부할 때 많이 봤던 한정된 자원, 상호 배제(뮤텍스), 기아, 데드락, 라이브락.. 식사하는 철학자들.. 이론보다는 더 자세한 코드 예시가 있었으면 좋았을 듯

다중 스레드를 쓰는 코드를 다양한 설정으로 실행하기 쉽게 구현하라.

  • 다중 스레드를 쓰는 코드 부분을 상황에 맞게 조율할 수 있게 작성하라
  • 프로세서 수보다 많은 스레드를 돌려봐라
  • 다른 플랫폼에서 코드를 돌려봐라 (모든 목표 플랫폼)
  • 코드에 보조 코드(instrument)를 넣어 돌려라. 강제로 실패를 일으키게 해봐라
    • 보조 코드를 추가하는 법
      1) 직접 구현하기: 코드에다 직접 wait(), sleep(), yield(), priority() 함수를 추가
      2) 자동화: AOF(Aspect-Oriented Framework), CGLIB, ASM 등과 같은 도구를 사용
  • SRP를 준수한다.
  • POJO를 사용해 스 레드를 아는 코드와 스레드를 모르는 코드를 분리한다.
  • 스레드 코드를 테스트할 때는 전적으로 스레드만 테스트한다.
  • 동시성 오류를 일으키는 잠정적인 원인을 철저히 이해한다.
  • 시용하는 라이브러리와 기본 알고리즘을 이해한다.
  • 보호할 코드 영역을 찾아내는 방법과 특정 코드 영역을 잠그는 방법을 이해한다. 잠글 필요가 없는 코드는 잠그지 않는다.
  • 스레드 코드는 많은 플랫폼에서 많은 설정으로 반복해서 계속 테스트해야 한다. 테스트용이성은 TDD(Test Driven Development) 3대 규칙을 따르면 자연히 얻어진다.
  • 초반부터 보조 코드를 고려한다. 스레드 코드는 출시하기 전까지 최대한 오랫동안 돌려봐야 한다.

'Study > Java' 카테고리의 다른 글

[Clean-Code] Chapter. 15-17(~G23)  (0) 2023.07.12
[Clean-Code] Chapter. 14  (1) 2023.07.11
[Clean-Code] Chapter. 7-10  (0) 2023.06.22
[Clean-Code] Chapter. 4-6  (0) 2023.06.21
[Clean-Code] Chapter. 1-3  (0) 2023.06.20

댓글