반응형

코드를 점진적으로 개선하기

 

목차

1. 책의 예제

2. 점진적으로 개선하기

3. IDE를 활용해 점진적으로 개선하기


1. 책의 예제

명령형 인수 구문 분석기

코드 초안

  - 모든 로직이 하나의 클래스에 들어가있다.

  - 처음부터 지저분한 코드를 짜려는 생각은 없었고, 코드를 어느정도 손봤지만 새로운 인수 유형이 들어오면서 재양이 시작됐다.

  - 이제는 개선해야 할 때라는 걸 깨닫고, 변경 전후 시스템이 동일하게 돌아간다는 사실을 확인하기 위해 테스트들을 작성해뒀다.

  - 자잘하게 점진적으로 개선해나갔다.

 

코드 완성본

  - Args 클래스에서 코드 중복을 최소화하고, ArgsException 클래스를 분리했다. ArgumentMarshaler 클래스를 통해 여러 인수에 대한 추후 확장성을 만들어냈다.

  - 코드만 분리해도 설계가 좋아진다. 관심사를 분리하면 코드를 이해하고 보수하기 훨씬 더 쉬워진다.

 

2. 점진적으로 개선하기

1. 코드가 나빠지고 있음을 느꼈을 때 기능을 추가하지 않고 개선을 시작한다.

2. 변경을 가한 후에도 시스템이 변경 전과 똑같이 돌아가야 한다. 테스트 코드가 없다면 작성하고, 코드를 수정하기 전 상태에서 모든 테스트가 통과해야 한다.

3. 책임에 따라 클래스를 나누고, 코드를 옮긴다. 테스트가 깨지지 않도록 확인하며 자잘한 변경을 조금씩 진행한다.

"프로그램을 망치는 가장 좋은 방법 중 하나는 개선이라는 이름 아래 구조를 크게 뒤집는 행위다"

 

3. IDE를 활용해 점진적으로 개선하기

Intellij 

mac 기준(Window는 command = Ctrl, option = Alt)

 

메서드 추출 기능 :                                     command + option + m

메서드 파라미터를 추가하거나 변경 기능 :  command + f6

메서드나 변수 이름 변경 :                          Shift + f6

변수 추출 기능 :                                        command + option + v

특정 값을 멤버 변수로 설정하는 기능 :       command + option + f

특정 값을 상수로 추출 기능 :                     command + option + c

 

하위 클래스의 메서드를 상위로 올리거나, 상위 클래스의 메서드를 하위로 내릴수 있는 기능 : 

해당 메서드에서 마우스 오른쪽 클릭 후 

 

 

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 13  (0) 2022.03.31
[Clean Code] Chapter 12  (0) 2022.03.28
[Clean Code] Chapter 11  (0) 2022.03.27
[Clean Code] Chapter 10  (0) 2022.03.26
[Clean Code] Chapter 09  (0) 2022.03.21
반응형

동시성을 구현할 때 명실할 것들

 

목차

1. 동시성 프로그래밍이란

2. 동시성 프로그래밍이 필요한 이유

3. 안전한 동시성 프로그래밍 규칙

4. 동시성 테스트 방법


1. 동시성 프로그래밍이란

어플리케이션을 효율적으로 실행하기 위해 멀티코어를 온전히 활용하도록 구현하는 방식

 

클라이언트가 아닌 어플리케이션 관점에서 봐야한다.

  - 내 어플리케이션의 효율성을 높여야 한다.

  - 어플리케이션이 동작하는 머신의 환경이 효율적으로 돌아가도록 어플리케이션에 메모리 누수나 자원이 낭비되지 않도록 신경쓴다

 

 

2. 동시성 프로그래밍이 필요한 이유

동시성 프로그래밍의 미신과 오해

 

동시성은 항상 성능을 높여준다.(x)

동시성은 때로 성능을 높여준다.(o)

 

동시성을 구현해도 설계는 변하지 않는다.(x)

동시성을 구현하면 설계를 바꿔야 한다.(o)

 

Web나 EJB와 같은 컨테이너를 사용하면 동시성을 이해할 필요가 없다.(x)

컨테이너를 사용해도 동시성을 이해해야 한다.(o)

 

 

3. 안전한 동시성 프로그래밍 규칙

단일 책임 원칙(SRP) 설계

 * 동시성 관련 코드는 다른 코드와 분리하라

  - 동시성 코드는 독자적인 개발, 변경, 조율 주기가 있다.

  - 동시성 코드에는 독자적인 난관이 있다. 다른 코드에서 겪는 난관과 다르며 훨씬 어렵다.

  - 잘못 구현한 동시성 코드는 별의별 방식으로 실패한다.

  - 주변에 있는 다른 코드가 발목을 잡지 않더라도 동시성 하나만으로도 충분히 어렵다.

 

자료 범위를 제한하라

 * 공유 자료를 최대한 줄여라

  - 동시 수정 문제를 피하기 위해 객체를 사용하는 코드 내 임계영역을 synchronized 키워드로 보호하라

  - 보호할 임계영역을 빼먹거나, 모든 임계영역을 보호했는지 확인하느라 수고가 드므로 임계 영역의 수를 최소화 해야 한다.

 

자료 사본을 사용하라

 * 공유 자료를 줄이려면, 최대한 공유하지 않는 방법이 제일 좋다.

  - 객체를 복사해 읽기 전용으로 사용한다.

  - 각 스레드가 객체를 복사해 사용한 후 한 스레드가 해당 사본으로 결과를 가져온다.

  - 사본을 사용하는 방식으로 내부 잠금을 없애 수행 시간을 절약하는 것이 사본 생성과 가비지 컬렉션에 드는 부하를 상쇄할 가능성이 크다.

 

Thread는 가능한 독립적으로 구현하라

 * 다른 스레드와 자료를 공유하지 않는다.

  - 서블릿처럼 각 Thread는 클라이언트 요청 하나를 처리한다.

  - 모든 정보는 비공유 출처(client의 request)에서 가져오며 로컬 변수에 저장한다.

  - 각 서블릿은 마치 자신이 독자적인 시스템에서 동작하는 양 요청을 처리한다.

 

라이브러리를 이해하라

 * java.util.concurrent 패키지를 익혀라

  - Thread Safe한 컬렉션을 사용한다. (ConcurrentHashMap, AtomicLong)

  - 서로 무관한 작업을 수행할 때는 executor 프레임워크르르 사용한다.

  - 가능하다면 Thread가 Blocking되지 않는 방법을 사용한다.

 

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

 * 공유 객체 하나에는 메서드 하나만 사용하라

  - 클라이언트에서 잠금 - 클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다.

    마지막 메서드를 호출할 때까지 잠금을 유지한다.

  - 서버에서 잠금 - 서버에다 "서버를 잠그고 모든 메서드를 호출한 후 잠금을 해제하는" 메서드를 구현한다.

     클라이언트는 이 메서드를 호출하기만 하면 된다.

  - 연결(Adapter)서버 - 잠금을 수행하는 중간 단계를 생성한다.

     '서버에서 잠금'방식과 유사하지만 원래 서버는 변경하지 않는다.

 

4. 동시성 테스트 방법

동시성 코드를 테스트 해야 한다.

 * 테스트를 했다고 동시성 코드가 100% 올바르다고 증명하기는 불가능하다. 하지만 충분한 테스트는 위험을 낮춘다.

  - 문제를 노출하는 테스트 케이스를 작성하라

  - 프로그램의 설정과 시스템 설정과 부하를 바꿔가며 자주 돌려라

  - 테스트가 실패하면 원인을 추적하라

  - 다시 돌렸더니 통과한다는이유로 그냥 넘어가면 절대로 안된다.

 

코드에 보조 코드를 넣어 돌려라

 * 드물게 발생하는 오류를 자주 발생시키도록 보조 코드를 추가한다.

  - 코드에 wait(), sleep(), yield(), priority() 함수를 추가해 직접 구현한다.

  - 보조코드를 넣어주는 조구를 사용해 테스트한다.

     다양한 위치에 ThreadJigglePoint.jiggle()를 추가해 무작위로 sleep(), yield()가 호출되도록 한다.

  - 테스트 환경에서 보조 코드를 돌려본다.

 

동시성 코드를 실제 환경이나 테스트 환경에서 돌려본다

 * 다양한 요청과 상황에 동시성 코드가 정상적으로 동작하는지 확인한다.

  - 배포하기 전에 테스트 환경에서 충분히 오랜시간 검증한다.

  - 동시성 코드를 배포한 후에 모니터링을 통해 문제가 발생하는지 지켜본다.

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 14  (0) 2022.04.02
[Clean Code] Chapter 12  (0) 2022.03.28
[Clean Code] Chapter 11  (0) 2022.03.27
[Clean Code] Chapter 10  (0) 2022.03.26
[Clean Code] Chapter 09  (0) 2022.03.21
반응형

창발적 설계로 깔끔한 코드 구현하기

 

목차

1. 창발적 설계란

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

3. 중복을 없앤다

4. 의도를 표현한다

5. 실용적 관점에서 타협한다.


1. 창발적 설계란

창발성(Emergence) :

  - 하위 계층에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출연하는 현상.

  - 각각의 개미는 집을 지을 능력이 없지만, 작은 개미들의 상호작용을 통해 집이라는 결과물이 나오는 것처럼 작은 요소들의 상호작용의 반복이 전체구조에 영향을 미친다.

 

창발적 설계 : 

  단순한 4가지를 반복하다보면 전체적으로 깨끗한 코드가 만들어진다.

 

'켄트 백' 이 주장한 창발적 설계 원칙

  1. 모든 테스트를 실행한다.
  2. 중복을 없앤다.
  3. 프로그래머 의도를 표현한다.
  4. 클래스와 메서드 수를 최소로 줄인다. 실용적 관점에서 타협한다.

원칙의 중요도는 위 순서와 같다.

 

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

  * 테스트를 작성할수록 설계 품질이 좋아진다.

  - 모든 테스트 케이스를 향상 통과하는 시스템은 '테스트가 가능한 시스템'이다. 테스트가 불가능한 시스템은 검증도 불가능하고, 절대 출시하면 안된다.

  - 테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 높아진다. 크기가 작고 목적 하나만 수행하는 클래스가 나온다.

  - 결합도가 높으면 테스트 케이스를 작성하기 어렵기 때문에 결합도를 낮추는 설계를 하게 된다.

  - '모든 테스트를 실행한다'는 규칙을 따르면 시스템은 낮은 결합도와 높은 응집력이라는 목표를 저절도 달성할 수 있다.

 

2. 중복을 없앤다

  * 기존의 코드를 최대한 재활용한다.

  - 중복을 없애는 패턴중에 Template Method 패턴 :

        알고리즘의 구조를 상위 클래스의 메서드에서 정의하고, 하위 클래스에서 자신의 맞게 세부 알고리즘을 정의한다.

 

3. 의도를 표현하라

  * 다른 사람을 위해서 읽기 쉽게 만들어야 한다. (본인이 다른 사람이 될 수도 있다.)

  1. 좋은 이름을 선택한다.
  2. 함수와 클래스 크기를 가능한 줄인다. 작은 클래스와 작은 함수는 이름 짓기도 쉽다!
  3. 표준 명칭을 사용한다. 다른 개발자가 보고 바로 이해할 수 있도록 디자인 패턴을 사용했다면 그 이름을 클래스에 넣어준다.
  4. 단위 테스트 케이스를 꼼꼼하게 작성한다.
  5. 다른 사람을 위해 조금이라도 더! 읽기 쉽게 만들려고 노력한다.

 

4. 클래스와 메서드 수를 최소로 줄인다. 실용적 관점에서 타협한다.

  * 과도한 설계를 하지 말아야 한다.

  - 여러가지 규칙을 극단적으로 심취해 클래스와 메서드를 무수하게 만들지 말라

  - 결국 좋은 코드를 만드는 이유는 생산성을 올리기 위한것이다.

  - 실용적인 관점에서 타협해야 한다.

  - 환경과 상황이 바뀌면 그때 설게를 하면 된다. 거의 발생하지 않을 일에 미리 투자하는 비용이 더 크다.

 

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 14  (0) 2022.04.02
[Clean Code] Chapter 13  (0) 2022.03.31
[Clean Code] Chapter 11  (0) 2022.03.27
[Clean Code] Chapter 10  (0) 2022.03.26
[Clean Code] Chapter 09  (0) 2022.03.21
반응형

관심사 분리 패턴들

 

목차

1. 관심사 분리

2. Dependency Injection (의존성 주입)

3. Cross Cutting Concerns (횡단 관심 분리)


1. 관심사 분리

constrction(생성)과 use(사용)은 아주 다르다.

  - 소프트웨어 시스템은(어플리케이션 객체를 제작하고 의존성을 서로 '연결'하는) 준비 과정과 (준비 과정 이후에 이어지는) 런타임 로직을 분리해야 한다.

  - 객체의 생성과 객체를 사용하는 부분을 분리한다.

 

시작에 대한 관심사 분리

 객체의 생성은 시작 단계에서, 비즈니스 로직은 객체를 사용하는데 집중한다.

  - 시작 단계는 모든 어플리케이션이 풀어야할 관심사이다.

  - main 함수에서 시스템에 필요한 객체를 생성한 후 어플리케이션에 넘긴다.

  - 어플리케이션은 그저 만들어진 객체를 사용한다.

  - 모든 객체가 잘 생성되었다고 가정하고, 객체를 이용한 개발에 집중할 수 있다.

 

요청에 대한 관심사 분리

Spring 프레임워크를 통해 요청에 대한 관심사를 분리해 요청 처리에 대한 비즈니스 로직에 집중할 수 있다. (Filter, Intercepter, AOP)

  - 서블릿 필터는 DispatcherSevlet 이전에 실행이 되는데 요청 내용을 변경하거나, 요청을 처리하기 전에 작업을 수행할 수 있다.

  - Filter와 Interceptor는 Servlet 단위에서 실행된다. 반면 AOP는 메소드 앞에서 Proxy Pattern으로 실행된다.

  - 인터셉터는 여러 개를 사용할 수 있고 로그인 처리, 권한체크, 프로그램 실행시간 계산작업, 로그확인 등의 업무처리에 활용된다.

  - AOP는 메서드 앞에서 Proxy Pattern으로 실행된다. 주로 '로깅', '트랜잭션', '에러처리' 등 비즈니스 단위 메서드에서 조금 더 세밀하게 조정하고 싶을 때 사용한다. AOP는 주소, 파라미터, 애노테이션 등 다양한 방법으로 대상을 지정할 수 있다.

 

 

2. Dependency Injection (의존성 주입)

객체 의존성을 DI 컨테이너에 맡긴다.  - Setter 메소드 or 생성자 인수를 통해 의존성 주입한다.  - DI 컨테이너는 요청이 들어올 때 필요한 객체의 인스턴스를 만든 후 의존성 설정한다.     ex) Spring IoC Container 

 

3. Cross Cutting Concerns (횡단 관심 분리)

  어플리케이션 전반에서 가지는 공통적인 관심사를 분리한다.

  - 비즈니스 로직 외에 Logging, Transaction 관리, Security 등 신경써야 할 관심사들이 많다.

  - 관심사들은 많은 어플리케이션 레이어에 퍼져있는데, 이 관심사들을 분리해 처리하는 것이 효율적이다.

 

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 13  (0) 2022.03.31
[Clean Code] Chapter 12  (0) 2022.03.28
[Clean Code] Chapter 10  (0) 2022.03.26
[Clean Code] Chapter 09  (0) 2022.03.21
[Clean Code] Chapter 08  (0) 2022.03.20
반응형

클래스 잘 설계하기

 

목차

1. 캡슐화되어야 한다

2. 단일 책임 원칙

3. 낮은 결합도, 높은 응집도

4. 변경하기 쉬워야 한다


1. 캡슐화되어야 한다

캡슐화 : 객체의 실제 구현을 외부로부터 감추는 방식

 

2. 단일 책임 원칙

클래스는 작아야 한다.

클래스가 맡은 책임이 한 개인가

  - 함수와 마찬가지로 클래스도 작아야 한다.

  - 함수는 라인 수로 크기를 측정했는데, 클래스는 맡은 책임의 수로 크기를 측정한다.

  - 클래스 설명은 단일(if), 그리고(and), 하며(or), 하지만(but)을 사용하지 않고 25단어 내외로 가능해야 한다.

     -> 책임이 한 가지여야 한다.

 

단일 책임 원칙(SRP) 중요성

  - 자잘한 단일 클래스가 많아지면 큰 그림을 이해하기 어렵다고 우려한다. 하지만 작은 클래스가 많은 시스템이든 큰 클래스가 몇 개뿐인 시스템이든 돌아가는 부품은 그 수가 비슷하다.

  - 큼직한 다목적 클래스 몇 개로 이뤄진 시스템은(변경을 가할 때) 당장 알 필요가 없는 사실까지 들이밀어 독자를 방해한다.

  - 작은 클래스는 각자 맡은 책임은 하나며, 변경할 이유가 하나며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.

 

3. 낮은 결합도, 높은 응집도

 * 결합도는 낮을수록 응집도는 높을수록 유지보수성이 좋다.

 

문제점 

결합도가 높은 클래스의 문제점 :

  - 연관된 클래스가 변경되면 수정이 필요하다.

  - 결헙도가 높으면 연관된 클래스들을 모두 이해해야 한다.

응집도가 낮은 클래스의 문제점 :

  - 여러 기능이 있으므로 이해하기 어렵다.

  - 재사용하기 어렵다.

 

 

4. 변경하기 쉬워야 한다

  - 추상화를 잘 활용해야 한다.

  - 기존의 클래스는 건들지 않고 수정되어야 한다. (OCP 원칙 위반되지 않게)

 

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 12  (0) 2022.03.28
[Clean Code] Chapter 11  (0) 2022.03.27
[Clean Code] Chapter 09  (0) 2022.03.21
[Clean Code] Chapter 08  (0) 2022.03.20
[Clean Code] Chapter 07  (0) 2022.03.17
반응형

깨끗한 테스트 코드

 

목차

1. 테스트 코드의 중요성

2. 테스트의 종류

3. Unit Test 작성

4. FIRST 원칙

5. 오픈소스 속 Unit Test


1. 테스트 코드의 중요성

  - 테스트 코드는 실수를 바로잡아준다.

  - 테스트 코드는 반드시 존재해야하며, 실제 코드 못지 않게 중요하다.

  - 테스트 케이스는 변경이 쉽도록 한다. 코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목이 바로 단위테스트다.

  - 테스트 케이스가 있으면 변경이 두렵지 않다. 테스트 케이스가 없다면 모든 변경이 잠정적인 버그다. 테스트 커버리지가 높을수록 버그        에 대한 공포가 줄어든다.

  - 지저분한 테스트 코드는 테스트를 안하니만 못하다.

 

<Effective Unit Test 책에서>

'테스트는 실사용에 적합한 설계를 끌어내준다.'

'테스트를 작성해서 얻게 되는 가장 큰 수확은 테스트 자체가 아니다. 작성 과정에서 얻는 깨달음이다.'

 

'테스트트 자동화되어야 한다.' - 매번 배포할 때마다 실행되어야 한다.

 

2. 테스트의 종류

Unit Test : 프로그램 내부의 개발 컴포넌트의 동작을 테스트한다. 배포하기 전에 자동으로 실행되도록 많이 사용한다.

Integration Test : 프로그램 내부의 개별 컴포넌트들을 합쳐서 동작을 테스트한다. Unit Test는 각 컴포넌트를 고립시켜 테스트하기 때문에 컴포넌트의 interaction을 확인하는 Integration Test가 필요하다.

E2E Test : End to End Test. 실제 유저의 시나이로대로 네트워크를 통해 서버의 Endpoint를 호출해 테스트한다.

 

3. Unit Test 작성

'테스트 라이브러리를 사용하자' (실무에서 JUnit5 + mockito를 많이 사용한다.)

 

Test Double

  * 테스트에서 원본 객체를 대신하는 객체

 

Stub :   - 원래의 구현을 최대한 단순한 것으로 대체한다.  - 테스트를 위해 프로그래밍된 항목에만 응답한다.

 

Spy :

  - Stub의 역할을 하면서 호출에 대한 정보를 기록한다.

  - 이메일 서비스에서 메시지가 몇 번 정송되는지 확인할 때

 

Mock :

  - 행위를 검증하기 위해 가짜 객체를 만들어 테스트하는 방법

  - 호출에 대한 동작을 프로그래밍할 수 있다.

  - Stub은 상태를 검증하고 Mock은 행위를 검증한다.

 

'given, when, then 패턴을 사용하자'

  - given : 테스트를 위한 pre-condition

  - when : 테스트하고 싶은 동작 호출

  - then : 테스트 결과 확인

4. FIRST 원칙

Fast : 빠르게

  - 테스트는 빨리 돌아야 한다. 자주 돌려야 하기 때문이다.

Independent : 독립적으로

  - 각 테스트를 독립적으로 작성한다. 서로에게 의존하면 실패한 원인을 찾기 어려워진다.(다른 테스트의 샐패로 인한건지, 코드 오류인지)

Repeatable : 반복가능하게

  - 테스트는 어떤 환경에서도 반복 가능해야 한다. 실제 환경, QA 환경, 모든 환경에서 돌아가야 한다.

Self-Validating : 자가검증하는

  - 테스트는 bool 값으로 결과를 내야 한다.

Timely : 적시에

  - 테스트하려는 실제 코드를 구현하기 직전에 구현한다.

 

4. 오픈소스 속 Unit Test

Trino(PrestoSQL) 프로젝트 코드

  - 책에서는 하나의 테스트에 하나의 assert를 사용하라고 했다.

  - 테스트하려는 인자가 많은 경우는 하나에 몰아서 하는 경우도 많다.

 

public class TestTypeCalculation
{
    @Test
    public void testBasicUsage()
    {
        assertEquals(Long.valueOf(42), calculateLiteralValue("42", ImmutableMap.of()));
        assertEquals(Long.valueOf(0), calculateLiteralValue("NULL", ImmutableMap.of()));
        assertEquals(Long.valueOf(0), calculateLiteralValue("null", ImmutableMap.of()));
        assertEquals(Long.valueOf(42), calculateLiteralValue("x", ImmutableMap.of("x", 42L)));
        assertEquals(Long.valueOf(42), calculateLiteralValue("(42)", ImmutableMap.of()));
        assertEquals(Long.valueOf(0), calculateLiteralValue("(NULL)", ImmutableMap.of()));
        assertEquals(Long.valueOf(42), calculateLiteralValue("(x)", ImmutableMap.of("x", 42L)));

        assertEquals(Long.valueOf(42 + 55), calculateLiteralValue("42 + 55", ImmutableMap.of()));
        assertEquals(Long.valueOf(42 - 55), calculateLiteralValue("42 - 55", ImmutableMap.of()));
        assertEquals(Long.valueOf(42 * 55), calculateLiteralValue("42 * 55", ImmutableMap.of()));
        assertEquals(Long.valueOf(42 / 6), calculateLiteralValue("42 / 6", ImmutableMap.of()));

        assertEquals(Long.valueOf(42 + 55 * 6), calculateLiteralValue("42 + 55 * 6", ImmutableMap.of()));
        assertEquals(Long.valueOf((42 + 55) * 6), calculateLiteralValue("(42 + 55) * 6", ImmutableMap.of()));

        assertEquals(Long.valueOf(2), calculateLiteralValue("min(10,2)", ImmutableMap.of()));
        assertEquals(Long.valueOf(10), calculateLiteralValue("min(10,2*10)", ImmutableMap.of()));
        assertEquals(Long.valueOf(20), calculateLiteralValue("max(10,2*10)", ImmutableMap.of()));
        assertEquals(Long.valueOf(10), calculateLiteralValue("max(10,2)", ImmutableMap.of()));

        assertEquals(Long.valueOf(42 + 55), calculateLiteralValue("x + y", ImmutableMap.of("x", 42L, "y", 55L)));
    }
}

 

code from - https://github.com/trinodb/trino/blob/master/core/trino-parser/src/test/java/io/trino/type/TestTypeCalculation.java

 

Junit5 Samples

  - @DisplayName은 테스트 클래스나 메서드에 보여질 이름을 입력하는 것이다. 테스트의 목적을 명확하게 작정할수 있다.

  - @ParameterizedTest는 하나의 테스트 메서드로 여러 가지 paramter를 테스트할 수 있다. @CsvSource의 값을 parameter로 넘       긴다.

  - JUnit5가 테스트에 관한 유용한 기능을 많이 가지고 있기 때문에 실무에서 많이 사용한다.

 

class CalculatorTests {

	@Test
	@DisplayName("1 + 1 = 2")
	void addsTwoNumbers() {
		Calculator calculator = new Calculator();
		assertEquals(2, calculator.add(1, 1), "1 + 1 should equal 2");
	}

	@ParameterizedTest(name = "{0} + {1} = {2}")
	@CsvSource({
			"0,    1,   1",
			"1,    2,   3",
			"49,  51, 100",
			"1,  100, 101"
	})
	void add(int first, int second, int expectedResult) {
		Calculator calculator = new Calculator();
		assertEquals(expectedResult, calculator.add(first, second),
				() -> first + " + " + second + " should equal " + expectedResult);
	}
}

 

code from - https://github.com/junit-team/junit5-samples/blob/main/junit5-jupiter-starter-gradle/src/test/java/com/example/project/CalculatorTests.java

 

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 11  (0) 2022.03.27
[Clean Code] Chapter 10  (0) 2022.03.26
[Clean Code] Chapter 08  (0) 2022.03.20
[Clean Code] Chapter 07  (0) 2022.03.17
[Clean Code] Chapter 06  (0) 2022.03.13
반응형

모호한 경계를 적당히 구분짓기

 

목차

1. 경계란

2. 경계 짓기 (1) 우리 코드를 보호하기

3. 경계 짓기 (2) 외부 코드와 호환하기

4. 외부 라이브러리 테스트하기 - Learning Test


1. 경계란

 여기서 말하는 경계는 우리 코드와, 외부 코드(오픈소스, 라이브러리)의 사이의 말한다.

  - 오픈 소스, 라이브러리를 안쓰는 프로젝트는 없다.

  - 우리가 만든 코드에 외부에서 들어온 코드를 병합해야 한다.

  - 외부 코드는 외부에서 만든 코드인데, 외부 시스템과 호출하거나 단순히 외부에서 만들어진 코드일 수 있다.

  - 우리 코드와 외부 코드를 깔끔하게 통합시키기 위해 경계를 잘 지어야 한다.

 

2. 경계 짓기 (1) 우리 코드를 보호하기

  캡슐화(Encapsulaltion) :

    객체의 실제 구현을 외부로부터 감추는 방식, 간단하게 말해서 'TMI하지 말아야한다.' 외부에 말할 필요가 없는 건 private 한다.

 

3. 경계 짓기 (2) 외부 코드와 호환하기

 외부 코드를 호출할 때 우리가 원하는 방식으로 사용하고 싶으면 어댑터 패턴(Adapter Pattern) 사용한다.

 

4. 외부 라이브러리 테스트하기 - Learning Test

   라이브러리를 '사용'하는 내가 라이브러리 테스트를 함으로서 생기는 장점

 

   외부 코드를 배우고, 안정성도 미리 검증할 수 있다.

    - 학습 테스트는 이해도를 높인다.

    - 외부 코드의 버전이 변경됐을 때, 우리 코드와 호환되는 지 확인할 수 있다.

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 10  (0) 2022.03.26
[Clean Code] Chapter 09  (0) 2022.03.21
[Clean Code] Chapter 07  (0) 2022.03.17
[Clean Code] Chapter 06  (0) 2022.03.13
[Clean Code] Chapter 05  (0) 2022.03.12
반응형

우아하게 예외 처리하기

 

목차

1. 예외 처리 방식

2. Unchecked Exception을 사용하라

3. Exception 잘 쓰기

4. 실무 예외 처리 패턴

5. 오픈소스 속 Exception 살펴보기


1. 예외 처리 방식

오류 코드를 리턴하지 말고, 예외를 던져라

 

2. Unchecked Exception을 사용하라

Exception을 상속하면 Checked Exception 명시적인 예외처리가 필요하다.

  - ex) IOException, SQLException

 

RuntimeException을 상속하면 Unchecked Exception 명시적인 예외처리가 필요하지 않다.

  - ex) NullPointerException, IllegalArgumentException 

 

<Effective Java> Exception에 관한 규약

  자바 언어 명세가 요구하는 것은 아니지만, 업계에 널리 퍼진 규약으로 Error 클래스를 상속해 하위 클래스를 만드는 일을 자제하자.

 

즉, 사용자가 직접 구현하는 unchecked throwable은 모두 RuntimeException의 하위 클래스여야 한다.

 

Exception, RuntimeException, Error를 상속하지 않는 throwable을 만들 수도 있지만, 이러한 throwable은 정상적인 사항보다 나을 게 하나도 없으면서 API 사용자를 헷갈리게 할 뿐이므로 절대로 사용하지 말자

 

c#, c++, python, 루비는 확인된 예외(Checked Exception)가 없음에도 불구하고 안정적인 소프트웨어를 구현하기에 무리가 없다.

 

3. Exception 잘 쓰기

  - 예외에 메시지 담자 :

        오류가 발생한 원인과 위치를 찾기 쉽도록, 예외를 던질 때는 전후 상황을 충분히 덧붙인다.

        실패한 연산 이름과 유형 등 정보를 담아 예외를 던진다.

  - exception wrapper

 

4. 실무 예외 처리 패턴

  - getOrElse :

      * 예외 대신 기본 값을 리턴한다.

         1. null이 아닌 기본 값 :

             복수형의 데이터를 가져올 때는 데이터의 없을 의미하는 컬랙션을 리턴하면 된다. null보다 size가 0인 컬렉션이 훨씬 안전한다.

         2. 도메인에 맞는 기본 값 :

             예외 처리를 데이터를 제공하는 쪽에서 처리해 호출부 코드가 심플해진다.

             코드를 읽어가며 논리적인 흐름이 끊기지 않는다.

             도메인에 맞는 기본값을 도메인 서비스에서 관리한다.

         3. 도메인에 맞는 기본값이 없다면? getOrElseThrow 이용

 

  - getOrElseThrow :

        * null 대신 예외를 던진다.(기본 값이 없다면)

           1. 데이터를 제공하는 쪽에서 null 체크를 하여, 데이터가 없는 경우엔 예외를 던진다.

           2. 호출부에서 매번 null 체크를 할 필요 없이 안전하게 데이터를 사용할 수 있다. 

           3. 호출부의 가독성이 올라간다.

 

   - 파라미터의 null을 점검하라 :

        1. null을 리턴하는 것도 나쁘지만 null을 메서드로 넘기는 것은 더 나쁘다.

        2. 메서드에서 null을 파라미터로 받지 못하게 한다.

 

   - 실무에서는 보통 자신의 예외를 정의한다 :

        1. 에러 로그에서 stacktrace 해봤을 때 우리가 발생시킨 예외라는 것을 바로 인지할 수 있다.

        2. 다른 라이브러리에서 발생한 에러와 섞이지 않는다. 기본 IllegalArgumentException을 던지는 것보다 우리 예외로 던지는게 어              느 부분에서 에러가 났는 지 파악하기 용이하다.

       3. 우리 시스템에서 발생한 에러의 종류를 나열할 수 있다.

   

5. 오픈소스 속 Exception 살펴보기

 apps-android-commons

 

public boolean findBookmark(Bookmark bookmark) {
        if (bookmark == null) {//Avoiding NPE's
            return false;
        } // null이면 예외를 던지지않고 false를 리턴한다.
          // Bookmark 존재여부에 대한 메서드의 목적에 부합하다.

        Cursor cursor = null;
        ContentProviderClient db = clientProvider.get();
        try {
            cursor = db.query(
                    BookmarkPicturesContentProvider.BASE_URI,
                    Table.ALL_FIELDS,
                    Table.COLUMN_MEDIA_NAME + "=?",
                    new String[]{bookmark.getMediaName()},
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                return true;
            }
        } catch (RemoteException e) {
            // This feels lazy, but to hell with checked exceptions. :)
            // ReomoteException을 RuntimeException으로 바꿔서 던졌다.
            throw new RuntimeException(e); 
        } finally {
            // finally 블록에서 리소스를 close 처리한다.
            // 리소스를 사용했다면 반드시 finally 블록에서 닫아줘야 한다.
            if (cursor != null) {
                cursor.close();
            }
            db.release();
        }
        return false;
    }

Code - https://github.com/commons-app/apps-android-commons/blob/f8a8f9207028a6fb5a35483c040821f22daf755e/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesDao.java

728x90
반응형

'Book > Clean Code' 카테고리의 다른 글

[Clean Code] Chapter 09  (0) 2022.03.21
[Clean Code] Chapter 08  (0) 2022.03.20
[Clean Code] Chapter 06  (0) 2022.03.13
[Clean Code] Chapter 05  (0) 2022.03.12
[Clean Code] Chapter 04  (0) 2022.03.12

+ Recent posts