1장 잘못된 구조의 문제 깨닫기
- 나쁜 구조의 폐해란
- 코드를 읽고 이해하는 데 시간이 오래 걸림
- 버그가 계속해서 발생함
- 나쁜 구조로 인해서 더 나쁜 구조가 만들어짐
1.1 의미를 알 수 없는 이름
- 좋지 않은 이름이 일으키는 악영향을 소개한다.
- 기술 중심 명명 : 기술 기반으로 이름을 붙이는 것
- class MemoryStateManager { void changeIntValue01(int changeValue) { ... } }
- 일련번호 명명 : 클래스와 메서드에 번호를 붙여서 이름 짓는 것
- class Class001 { void method001(); void method002(); void method003(); }
- 위와 같이 이름을 지으면 코드에서 어떠한 의도도 읽어 낼 수 없다.
- 읽고 이해하는 데 시간이 오래걸린다.
- 충분히 이해하지 못한 상태로 코드를 변경하면 버그가 발생한다.
- 의도와 목적을 드러내는 이름을 사용하기만 해도 구조가 간단하고 명확해진다.
1.2 이해하기 어렵게 만드는 조건 분기 중첩
- 중첩문이 많을 수로 가독성이 나빠진다. 어디서부터 어디까지가 if 조건문 처리 블록인지 확인하기 힘들기 때문이다.
- 조건이 복잡해질수록 코드를 읽고 이해하기 힘들다.
- 이해가 힘들면 디버깅과 기능 변경에 더 오랜 시간이 걸린다.
- 게다가 로직을 정확하게 이해하지 못하고 기능을 변경하면 버그가 발생할 수도 있다.
1.3 수많은 악마를 만들어 내는 데이터 클래스
- 데이터 클래스의 어떤 점이 나쁜지 살펴보자.
- 업무 계약을 다루는 서비스에서 계약 금액을 처리하는 요구사항을 클래스로 구현하는 상황
// 계약 금액 public class ContractAmount { public int amountIncludingTax; // 세금 포함 금액 public BigDecimal salesTaxRate; //소비세율 }
- public 인스턴스 변수를 갖고 있으므로 클래스 밖에서도 데이터를 자유롭게 변경할 수 있는 구조
- 이처럼 데이터를 갖고 있기만 하는 클래스를 데이터 클래스라고 한다.
- 요구사항에는 세금이 포함된 금액을 계산하는 로직도 필요한데, 계산 로직을 데이터 클래스가 아닌 다른 클래스에 구현하는 일이 벌어지곤 한다. 설계를 따로 고려하지 않아 생기는 일이다.
// 계약을 관리하는 클래스 public class ContractManager { public ContractAmount contractAmount; // 세금 포함 금액 계산 public int calculateAmountIncludingTax() { ... } // 계약 체결 public void conclud() { ... } }
- 위 코드의 문제점을 알아보자
1.3.1 사양을 변경할 때 송곳니를 드러내는 악마
- 업무 계약 서비스에서 소비세와 관련된 사양이 변경되어, 소비세율과 관련된 로직을 변경했다.
- 그런데 며칠이 지나 소비세율이 변경되지 않았다는 장애 보고가 올라왔다. 원인을 조사해보니, 다른 곳에도 세금 포함 금액을 계산하는 로직이 존재하고 있었다.
- 담당자는 소비세와 관련된 부분을 소스 코드 전체에서 찾기 시작했는데, 놀랍게도 세금 포함 금액을 계산하는 로직이 수십 곳에 있었다.
- 왜 이런 일이 생긴 것일까? 세금 포함 금액은 여러 상황에서 필요하므로 여러 곳에 구현되기 쉽다.
- 계산 로직을 어느 한 곳에 만들어두면, 사람들이 모두 그것만 사용하고 따로 구현하지 않겠지라고 생각할 수도 있다. 하지만 설계에 관심이 없다면, 유지보수하는 사람이 많을수록 이미 구현되어 있다는 사실을 모르고 따로 구현해버릴 수도 있다.
- 이런 상황은 데이터를 담고 있는 클래스와 데이터를 사용하는 계산 로직이 멀리 떨어져 있을 때 자주 일어난다.
- 이처럼 데이터와 로직이 분산되어 있는 것을 응집도가 낮은 구조라고 한다.
1.3.2 응집도가 낮아 생길 수 있는 여러 가지 문제를 살펴 보자.
- 코드 중복
- 수정 누락
- 가독성 저하
- 코드가 분산되어 있으면 중복된 코드를 포함해서 관련된 정보를 다 찾는 것만으로도 시간이 오래 걸린다.
- 초기화되지 않은 상태(쓰레기 객체)
- 코드를 실행하면 NPE(Null Point Exception)가 발생한다. 소비세율 salesTaxRate 은 BigDecimal 타입이므로 따로 초기화하지 않으면 null이 들어간다.
- 이처럼 초기화하지 않으면 쓸모없는 클래스 또는 초기화하지 않은 상태가 발생할 수 있는 클래스를 안티 패턴 쓰레기 객체 라고 부른다.
- ContractAmount amount = new ContractAmount(); System.out.println(amount.salesTaxRate.toString());
- 잘못된 값 할당
- 현재 데이터 클래스는 소비세율을 음수로 대입해도 값이 들어간다. 따라서 잘못된 값이 쉽게 들어갈 수 있는 구조이다.
ContractAmount amount = new ContractAmount(); amount.salesTaxRate = new BigDecimal("-0,1");
- 잘못된 값이 들어가지 않게 데이터 클래스를 사용하는 로직을 살짝 변경해서 유효성 검사하게 만들수 있지만, 사용하는 곳마다 검사 로직을 추가해야 하니 여러 곳에 코드가 중복될 수 있다.
- 그렇게 되면 요구사항이 변경될 때마다 수정 누락과 가독성 저하같은 문제가 계속 발생한다.
'내 코드가 그렇게 이상한가요?' 카테고리의 다른 글
6장 조건 분기: 미궁처럼 복잡한 분기 처리를 무너뜨리는 방법 (0) | 2024.03.30 |
---|---|
5장 응집도: 흩어져 있는 것들 (0) | 2024.03.28 |
4장 불변 활용하기: 안정적으로 동작하게 만들기 (0) | 2024.03.28 |
3장 클래스 설계: 모든 것과 연결되는 설계 기반 (0) | 2024.03.21 |
2장 설계 첫걸음 (변수명) (0) | 2024.03.12 |