[TDD, 클린 코드] (2) TDD, OOP를 위한 원칙들

June Lee·2023년 5월 2일
0

좋은 코드

목록 보기
4/8
  • 테스트하기 어려운 코드를 object graph의 상위쪽으로 옮긴다 (옮기기 전 단계는 결국 테스트할 수 없어지지만, 우리는 모든 코드를 테스트하는게 목표는 아님)
    -> 테스트 하기 어려운 부분을 분리해서 핵심로직을 테스트하는게 목적

  • 메소드의 크기가 최대 10라인을 넘지 않도록 구현

  • method가 한 가지 일만 하도록 최대한 작게 만들어라.

  • TDD를 잘하려면 특정 시점의 상태를 잘 생각해내야함.
    - 예를 들어, 우승자를 구하는 로직을 테스트한다면, 경기가 끝난 상태의 자동차들을 Given으로 줘야함.

  • 모델(도메인) 객체에서는 getter, setter 쓰지 마라. DTO, 혹은 view에 전달할 때는 getter 허용

  • 생성자는 다른 생성자를 호출하도록 구현

  • 테스트 코드를 위해 만든 생성자로 잘못 세팅하는 경우가 생기는걸 막으려면… 팩토리 클래스를 만들어서 실제 관리 포인트를 하나로 만드는 방법도 있다.

public class carFactory {
	public static void create() {

	}
}
  • 생성자는 마음껏 늘리면서 public 메소드의 수는 최대한 줄여라

  • 접근제어자는 최소한만 오픈 -> 필요하면 더 열기.

  • 상태값이 없는 클래스의 메서드는 Static으로 해도 괜찮다

  • 가장 좋은건 클래스의 인스턴스 변수가 클래스 내 모든 메서드에서 쓰일 수 있도록 설계되는 것.

  • 테스트 하기 어려운 부분은 쿨하게 테스트 안해도 됨 -> 테스트 하기 쉬운 부분과 어려운 부분을 분리하는 연습 필요(출력, DB CRUD.. 이런건 엔드투엔드 테스트, 통합테스트에서 해야함. 단위테스트X. 핵심 비즈니스로직이 없음)

  • 모킹은 가능한 사용하지 않기(가독성이 떨어지고 유지보수가 어렵다)

  • 자바에서도 final 적극 활용 (ex_변경되지 않는 메서드의 매개변수..)

  • UI, DB 등과 의존관계를 가지지 않는 핵심 도메인 영역을 집중 설계

  • view <-> controller <-> domain
    도메인과 뷰는 의존관계를 갖지 않는다.
    갖더라도 view -> domain 방향만 허용

  • 테스트는 경계값으로 (모든 값으로 할 수 없기 때문에)

  • total, sub,average, max, min, record, string, pointer 등 한정자를 써야한다면 이름의 끝에! (ex. expenseTotal..)

  • 오픈소스 코드를 많이 읽어보기 (JDK, Spring 프레임워크 등)

  • 도메인 지식을 쌓기 위해 노력

  • private 메서드가 늘어나는 것에 대한 거부감을 없애기

  • private 메서드들은 퍼블릭을 통해 테스트 하기.
    priavte에 로직이 너무 많다면 여러 클래스의 역할을 하나에서 하는게 아닌지 의심해봐야함.

  • 클래스의 인스턴스 변수를 최대한 줄여라 (즉 클래스가 가지는 상태값.) -> 상태값을 여러군데에서 유지하면 변경될때마다 바꿔줘야함. 데이터 중복의 문제.. (ex) cars와 winners를 따로 가질 필요 없음. 그냥 필요할 때 winners를 구하는게 훨씬 좋음)

  • 비즈니스 로직과 UI 로직을 분리(ex. car에서 print 메서드를 갖는것)

  • fixture -> 테스트를 위해 필요한 초기 데이터. 테스트의 인스턴스 변수는 모든 테스트에 공통으로 필요한 픽스쳐만. 테스트마다 달라지는거는 각 테스트로 옮기기
    @BeforeEach에는 각 테스트에서 중복으로 사용하는 픽스쳐만 초기화

  • 한번 만들어진 후 외부에 의해 값을 바꿀 수 없는 Immutable 객체로 바꾸기
    - ex) 리턴할때 new Position(n) 과 같은 식으로 반환 -> 인스턴스가 많이 만들어진다는 단점. 이정도의 성능 저하는 이슈가 되면 그때 바꿔도 괜찮음.

  • 값의 유효 범위 -> 객체에서 관리

  • 무항 메서드를 지향하기. 3개 이상은 가급적 사용X. 4개 이상은 절대x(특별한 이유가 있어도)

  • 3개 이상의 인스턴스 변수를 가진 클래스를 만들지 마라
    -> 너무 많으면 관련있는 애들을 묶어서 새로운 클래스로 만들기


클래스 분리의 정량적 기준

  1. 모든 원시값과 문자열을 포장 -> ex RandomNumber
  2. 일급 콜렉션 사용

cf) cqrs

값을 바꾸도록 쿼리하는 부분과 값을 가져오는 부분을 분리하는 것과 분리하지 않는것.
1)
public Position move() {
position = position + 1;
return this;
}

2)
public void move() {
position = position + 1;
}

public int getPosition() {
return position;
}
뭐가 더 맞고 어떤 상황에서 뭐가 더 좋은지 본인이 기준을 찾기.. 백프로 정답은 없음.


인터페이스 추출이 필요한 경우

구현체가 실제 코드 쪽에 1개, 그리고 테스트 쪽에도 존재
-> 이게 결국 DI(의존 관계 주입). 이걸 잘써야 유연하고 테스트 쉬운 코드가 만들어짐
-> 자바 8에 있는 람다(함수를 인자로 전달 == 인터페이스의 구현체, 익명 클래스, 구현해야할 메소드가 하나일때만 람다로 대체 가능, @FuntionalInterface 를 통해 강제 가능)를 쓰면 코드가 매우 간결해짐

profile
📝 dev wiki

0개의 댓글