클린 코드(Clean Code)/좋은 코드, 나쁜 코드 요약

7장 - 코드를 오용하기 어렵게 만들라

Junuuu 2023. 12. 16. 00:01
반응형

코드를 잘못 사용할 수 있는 몇 가지 사례

  • 호출하는 쪽에서 잘못된 입력
  • 다른 코드의 부수 효과 (입력 매개변수 수정 등)
  • 정확한 시간이나 순서에 따라 함수를 호출하지 않음
  • 관련 코드에서 가정과 맞지 않게 수정 발생

 

 

코드를 오용하게 어렵게 만드는 방법

  • 불변 객체로 만드는 것을 고려하자 - 가변 객체는 추론하기 어렵고, 다중 스레드에서 문제가 발생할 수 있음
  • 이때 객체 내부의 List 등은 가변으로 내버려 두는 실수를 할 수 있는데 깊은 수준까지 불변적으로 만드는 것을 고려하면 좋습니다.
  • Int, Double과 같은 일반 데이터 유형보다 Moeny과 같은 value class 등을 고려해 보자
  • 시간등을 활용할 때는 ms, second 등 다양하게 활용된다 Duration 객체와 같은 것들을 고려해 보자
  • 데이터에 대한 진실의 원천은 하나여만 한다
  • 논리에 대한 진실의 원천은 하나여만 한다

 

데이터에 대한 진실의 원천은 하나여만 한다

데이터에 대한 진실이 2개로 증가하고 2개가 달라지는 순간 고약한 버그로 이어질 수 있다.

다만, 데이터를 계산하는데 시간이 너무 오래 걸린다면 지연된 계산 결과를 캐싱해 두어도 좋다.

 

논리에 대한 진실의 원천은 하나여만 한다 

예를 들어 직렬화, 역직렬화를 수행하는 코드가 존재할 때 직렬화하는 부분만 수정된다면 역직렬화를 수행할 때 예외가 발생할 수 있습니다.

serialize(), deserialize() 두 가지 함수가 갖는 Format 클래스를 정의하여 이 작업을 수행해 준다면 이런 상황을 막아낼 수 있습니다.

 

코드 예제 - 논리에 대한 진실의 원천이 2개

class DataLogger{
	private final List<Int> loggedValues;
    ...
    
    String serializedValues = loggedValues
    	.map(value -> value.toString(Radix.BASE_10))
        .join(",")
   ...     
}

class DataLoader{
    
    List<Int> loadValues(FileHandler file){
        return file.readAsString()
        .split(",")
        .map(str -> Int.parse(str, Radix.BASE_10)

}

serialize 할 때 join 하는 부분을 ", "에서 "."으로 변경한다면 deserialize 할 때 예외가 발생할 것입니다.

 

코드 예제 - 논리에 대한 진실의 원천이 1개

class IntListFormat{
    private const String DELIMITER = ",";
    private const Radis RADIX = Radix.BASE_10;
    
    String serialize(List<Int> values){
        return values.
    	    .map(value -> value.toString(RADIX))
            .join(DELIMITER)
    }    
    
    List<Int> deserialize(String serialized){
        return serialized
            .split(DELIMITER)
            .map(str -> Int.parse(str, RADIX)
    }
}

이제 논리에 사용되는 모든 상수들을 공유하기 때문에 예외가 발생할 가능성이 현저하게 줄어듭니다.