[워밍업 클럽 스터디 2기 :: BE 클린코드 & 테스트] 1주차 발자국

[워밍업 클럽 스터디 2기 :: BE 클린코드 & 테스트] 1주차 발자국

 

뇌 메모리 적게 쓰기

프로그램 = 데이터 + 코드

정리하는 뇌

  • 정리 시스템에서 중요한 과제는 최소의 인지적 노력으로 최대의 정보를 제공하는 것이다.

도둑맞은 집중력

  • 뇌는 한 번에 한 가지 일 밖에 하지 못한다.

인지적 경제성을 추가하자


Early return

Early return 으로 else의 사용을 지양하자

Early return 을 통해서 끊어서 볼 수 있도록 하여 이후 불필요한 로직을 수행하지 않도록 처리


사고의 depth 줄이기

  • 중첩 분기분, 중첩 반복문

    • ‘무조건 1 depth로 만들어라’ 가 아니다. → 보이는 depth를 줄이는 데에 급급한 것이 아니라, 추상화를 통한 사고 과정의 depth를 줄이는 것이 중요 → 2중 중첩 구조로 표현하는 것이 사고하는 데에 더 도움이 된다고 판단한다면, 메서드 분리보다 그대로 놔두는 것이 더 나은 선택일 수 있다. 때로는 메서드를 분리하는 것이 더 혼선을 줄 수도 있다.

    • 무조건 strame 이 인지하기 좋은 것은 아니다. for i, foreach 가 더 좋은 경우도 있으니 상황에 맞게 사용하자

  • 사용할 변수는 가깝게 선언하기

    • Before

      int i = 10;
      
      // 코드 20줄.....
      
      int j = i + 10;
      
    • After

      // 코드 20줄.....
      
      int i = 10;
      int j = i + 10;
      

파라미터를 줄일 수 있다면 줄이는게 좋다.


공백 라인을 대하는 자세

  • 공백 라인도 의미를 가진다. → 복잡한 로직의 의미 단위를 나우어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.


부정어를 대하는 자세

  • 부정어구를 쓰지 않아도 되는 상황인지 체크

  • 부정의 의미를 담은 다른 단어가 존재하는지 고민 or 부정어구로 메서드명 구성

※ 부정 연산자(!)의 가동성 ↓

if(!isLeftDirection()){
}

// 반대대는 상황이 있다면
if(!isRightirection()){
}
// 메서드명에 Not 을 추가하는 방법
if(isNotLeftDirection()){
}

해피 케이스와 예외 처리

사람은 해피 케이스에 몰두하는 경향이 있다.

  • 예외가 발생할 가능성 낮추기

  • 어떤 값의 검증이 필요한 부분은 주로 외부 세계와의 접점

  • 의도한 예외와 예상하지 못한 예외를 구분하기

Null을 대하는 자세

  • 항상 NullPointException을 방지하는 방향으로 경각심 가지기

  • 메서드 설계 시 return null을 자제한다. → 만약 어렵다면, Optional 사용을 고민해 본다.

Optional에 관해

  • Optional은 비싼 객체다. 꼭 필요한 상황에서 반환 타입에 사용한다.

  • Optional을 파라미터로 받지 않도록 한다. 분기 케이스가 3개나 된다.
    (Optional이 가진 데이터가 null인지 아닌지 + Optional 그 자체가 null인지)

  • Optional을 반환받았다면 최대한 빠르게 해소한다.

  • 잘못 사용된 Optional은 안티패턴이다.
    (JPA에서의 Optional 은 잘 사용된 패턴)

Optional을 해소하는 방법

  • 분기문을 만드는 isPresent()-get() 대신 풍부한 API 사용

  • orElse(), orElseGet(), orElseThrow()의 차이 숙지

    • orElseThrow() : 그냥 쓰면 됨

    • orElse() : 항상 실행, 괄호안의 값이 확정된 값일때 활용

    • orElseGet() : null인 경우 실행, 값을 제공하는 동작(Supplier) 저의

public T orElse(T other){
	return value != null ? value : other;
}

Intger result = somethingOptional.orElse(methodHeavy()); // 호출할 필요가 없는 경우에도 항상 실행
Intger result3 = somethingOptional.orElse(0); // 정해진 값일 때 사

public T orElseGet(Supplier<? extends T> supplier){
	return value != null ? value : supplier.get();
}

integer result = somethingOptional.orElseGet(() -> methodHeavy()); // null인 경우에만 실행
  • 예외를 던지고 그 안에 메세지를 넣어서 무슨 오류인지 인지할 수 있게 도와주자

  • 비교를 할 때 상수와 변수를 비교해야 한다면 상수를 앞에 넣어서 NullPointException 이 안 일어나게 할 수도 있다


추상의 관점으로 바라보는 객체 지향

객체 : 추상화된 데이터 + 코드

관심사의 분리

  • 유지 보수가 원활해진다.

  • 높은 응집도, 낮은 결합도


객체 설계하기

객체 : 데이터 + 코드

객체(Object)로 추상화 하기

  • 비공개 필드(데이터), 비공개 로직(코드, 기능 구현부)

  • 공개 메서드 선언부를 통해 외부 세계와 소통 → 각 메서드의 기능은 객체의 책임을 드러내는 창구

  • 객체의 책임이 나뉨에 따라 객체 간의 협력이 발생

객체가 제공하는 것

  • 절차 지향에서 잘 보이지 않았던 개념을 가시화

  • 관심사가 한 군데로 모이기 때문에, 유지보수성 ↑ →
    ex) 객체 내부에서 객체가 가진 데이터의 유효성 검증 책임을 가질 수 있다.

  • 여러 객체를 사용하는 입장에서는 구체적인 구현에 신경 쓰지 않고 보다 높은 추상화레벨에서 도메인 로직을 다룰 수 있다.

새로운 객체를 만들때 주의할 점

  • 1개의 관심사로 명확하게 책임이 정의되었는지 확인하기 → 메서드를 추상화 할 때와 비슷하다. → 객체를 만듦으로써 외부 세계와 어떤 소통을 하려고 하는지 생각해보자.

  • 생성자, 정적 팩토리 메서드에서 유효성 검증이 가능하다. → 도메인에 특화된 검증 로직이 들어갈 수 있다.

Setter 사용 자제

→ 데이터는 불변이 최고다. 변하는 데이터라도 객체가 핸들링 할 수 있어야 한다.

→ 객체 내부에서 외부 세계의 개입 없이 자체적인 변경/가공으로 처리할 수 있는지를 확인

→ 만약 외부에서 가지고 있는 데이터로 변경 요청을 해야하는 경우, ‘set~’이라는 단순한 이름 보다는 ‘update~’ 같이 의도를 드러내는 네이밍을 고려하자.

Getter도 처음에는 사용 자제. 반드시 필요한 경우에 추가하기

→ 외부에서 객체 내 데이터가 필요하다고 getter를 남발하는 것은 무례한 행동이다.

객체에 메시지를 보내라


단일 책임의 원칙 :

SRP : Single Responsibility Principle

하나의 객체는 각자 맞는 책임을 가지게

OCP : Open-Closed Principle

추상화와 다형성을 이용!

  • 객체 자체는 무엇이 들어와도 처리가능 하게

  • 인터페이스를 통해 다양한 값이 들어 올 수 있도록 처리

LSP : Liskov Subsitution Principle

부모에서의 기능을 자식에서도 동일하게

ISP : Interface Segregation Principle

  • 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다. → 인터페이스를 잘게 쪼개라

  • ISP를 위반하면, 불필요한 의존성으로 인해 결합도가 높아지고, 특정 기능의 변경이 여러 클래스에 영향을 미칠 수 있다.

※ 기능단위로 인터페이스를 작게 사용해라!

 

DIP : Dependency Inversion Principle

의존성 : 하나의 모듈이 다른 하나의 모듈을 생성하거나 사용하는 모든 것, 참조하는 것

의존성 순방향 : 고수준 모듈이 저수준 모듈을 참조

댓글을 작성해보세요.

채널톡 아이콘