10장 이름 설계: 구조를 파악할 수 있는 이름

hjoon·2024년 5월 10일
0

이름 설계: 구조를 파악할 수 있는 이름

10.1 악마를 불러들이는 이름

온라인 쇼핑몰 예시

온라인 쇼핑몰에는 예약, 주문, 출고, 발송관련 유스케이스가 존재한다.

만약 4가지 유스케이스와 관련된 클래스 이름을 상품이라 지어버리면 해당 클래스에 여러 클래스와 관련있는 로직이 담기고 거대해진다. 클래스가 거대해지면 사양 변경이 힘들고 버그가 발생할 확률이 커진다.

10.1.1 관심사 분리

여러 클래스와 관련이 있는 상품 클래스는 강한 결합 구조이다.
결합이 느슨하고 응집도가 높은 구조로 만들기 위해 관심사(유스케이스, 목적, 역할) 분리가 필요하다.

10.1.2 관심사에 맞는 이름 붙이기

관심사에 따라 분리하기 위해 가장 첫번째로 해야할 작업이 관심사에 맞는 이름 붙이기이다

  • 상품이란 클래스를 예약, 주문, 출고, 발송 유스케이스에 따라 예약 상품, 주문 상품, 출고 상품, 발송 상품으로 분리해 볼 수 있다.

10.1.3 포괄적이고 의미가 불분명한 이름

상품과 같이 클래스명이 포괄적이라면 온갖 로직이 구현되게 된다.
이 처럼 이름이 포괄적이여서 목적이 불분명한 클래스를 목적 불명 객체라 부를 수 있다.
목적이 불명한 객체가 생성되지 않도록 관심사에 맞는 이름 설계를 해야 한다.

클래스 이름을 잘 설계하려면 => 관심사 분리가 필요하다
관심사 분리를 쉽게 하려면 => 비지니스 목적에 따라 이름을 지어보면 된다


10.2 이름 설계하기 - 목적 중심 이름 설계

관심사 분리를 생각하고 비지니스 목적에 맞게 이름을 설계의 장점

  • 높은 가독성
  • 결합이 느슨하고 응집도가 높은 구조를 만듦
  • 이름만으로 목적과 의도를 알기 쉬움

10.2.1 최대한 구체적이고, 의미 범위가 좁고, 특화된 이름 선택하기

클래스 이름을 비지니스 목적에 특화된 이름으로 짓을 경우 얻을 수 있는 효과

  • 이름과 관계 없는 로직을 배제할 수 있다 -> 클래스가 작아진다
  • 관계된 클래스 개수가 적어진다 -> 결합도가 낮아짐 -> 사양 변경시 영향 범위가 좁아짐
  • 개발 생산성이 향상됨

10.2.2 존재가 아니라 목적을 기반으로 하는 이름 생각하기

존재 기반 이름

  • 목적이 불분명하다 -> 이름이 여러 곳에서 사용되기 쉽다 -> 로직레벨에서 혼란을 준다

목적 기반 이름

  • 목적이 확실하다 -> 관심사 분리가 명확해진다 -> 로직레벨에서 가독성을 높혀준다.

10.2.3 어떤 비지니스 목적이 있는지 분석하기

소프트웨어가 추구하는 목적을 분석해야 비지니스 목적에 특화된 이름을 만들 수 있다

  • 온라인 쇼핑몰 : 판매 제품, 주문, 발송, 캠페인
  • 게임 : 무기, 몬스터, 아이템, 기간 이벤트
  • SNS : 메시지, 팔로워, 타임라인

10.2.4 소리 내어 이야기해 보기

프로그램에 문제가 발생했을 때, 누군가에게 설명하다 보면 스스로 원인을 깨닫는다는 고무 오리 디버깅 방식처럼 이야기를 나누다보면 관심사를 더 수집하고 비지니스에 대해 자세히 알 수 있게 된다.

  • 비지니스 측면을 잘 이해하고 있는 사람과 이야기해 보자.

이야기하고 분석하는 활동을 유비쿼터스 언어라 설명한다. (도메인 주도 설계 책에서)
팀에서 유비쿼터스 언어를 사용할 때 계속해서 대화하고 이름을 다듬어나가는 것이 중요하다.

10.2.5 이용 약관 읽어보기

이용 약관에는 서비스와 관련된 규칙들이 엄격한 표현으로 작성되어있다.
이를 참고하여 클래스 이름을 설계할 수 있다.
비지니스 규칙과 클래스를 일치하게 만들면 정확하고 빠르게 변경할 수 있다.

10.2.6 다른 이름으로 대체할 수 없는지 검토하기

의미를 더 좁게 만들 수 없는지, 이상한 점이 없는지 검토하는 것이 좋다.

ex) 호텔 숙박 예약 시스템 유저 클래스 이름

  • 사용자 : 의미가 너무 넓다
  • 고객 : 숙박하는 사람과 숙박 요금을 결제하는 사람을 구분지을 수 있을 만큼 세분화가 필요하다

투숙객, 결제자 : '사용자'보다 의미가 좁은 이름으로 이름을 변경해볼 수 있다.

10.2.7 결합이 느슨하고 응집도가 높은 구조인지 검토하기

목적에 특화된 이름을 선택하면 목적 이외의 로직을 배제하기 쉬워진다.

  • 관련된 로직만 모이므로 응집도가 높아진다

관련있는 클래스가 많다면 좋지 않은 징조이다

  • 의미를 더 좁힐 수 있는 특화된 이름을 설계하여 강한 결합 상태를 느슨하게 만들어보자

10.3 이름 설계 시 주의 사항

10.3.1 이름에 관심 갖기

이름과 로직을 대응시킨다, 이름이 프로그램 구조를 크게 좌우한다는 사실을 팀원들과 이야기해야 한다.

10.3.2 사양 변경 시 '의미 범위 변경' 경계하기

반복되는 사양 변경에 의해 개발 맥락에서 말이 의미하는 바가 변화한다
그에 따라 이름 설계도 다시 검토해봐야 한다.

  1. 개발 초기 : '고객' 클래스가 '개인 고객'만 의미
  2. 개발 사양 변경 : '고객' 클래스가 '개인 고객', '법인 고객'을 포함하게 됨
  3. 이름 재설계 : '고객' 클래스를 '개인 고객', '법인 고객' 클래스로 분리

10.3.3 대화에는 등장하지만 코드에 등장하지 않는 이름 주의하기

이름 없는 로직 (ex. 문제가 있는 회원)

  • 중요한 개념이지만 소스 코드에서 이름이 붙어있지 않고 로직에 묻혀 있는 경우 가 꽤 많다.
  • 이름 없는 로직은 소스 코드 여러곳에서 무분별하게 작성되는 경향이 있다
  • 이름 없는 로직에 대한 사양이 변경됐을 때, 변경하기가 어려워진다.

이름 없는 로직을 지양하고 메소드와 클래스로 설계하도록 하자.

10.3.4 수식어를 붙여서 구별해야 하는 경우는 클래스로 만들어 보기

버그 상황

// 악세서리에 최대 히트포인트 증가 효과가 있으므로
// 악세서리의 최대 히트포인트 증가 효과 적용
int maxHitPoint = member.maxHitPoint + accessory.maxHitPointIncrements();

// 방어구에 최대 히트포인트 증가 효과가 추가되어
// 다른 위치에 히트 포인트 증가 효과 적용 로직 추가 (버그 발생)
maxHitPoint = member.maxHitPoint + armor.maxHitPointIncrements();

버그 발생

  • accessory의 최대 히트포인트가 반영되지 않고 armor의 최대 히트포인트만 반영되어짐.

버그 발생 원인

  • member.maxHitPoint의 의미는 아이템(악세서리, 방어구, 회복 아이템)에 의한 증가 효과가 전혀 반영되지 않은 '캐릭터의 원래 최대 히트 포인트'이다. 하지만 팀원이 member.maxHitPoint를 아이템에 의한 증가효과가 누적된 히트포인트로 잘못 이해하여 버그가 발생했다.

문제점

  • maxHitPoint가 캐릭터의 원래 최대 히트포인트인지 장비 착용으로 높아진 최대 히트포인트인지 이름만 보고 알 수 없다.
해결방법
  1. 이름을 재설계
  • 캐릭터의 원래 최대 히트 포인트 -> originalMaxHitPoint
  • 장비 착용으로 높아진 최대 히트 포인트 -> correctedMaxHitPoint
  1. 이름을 변경하는걸 넘어서 클래스로 설계
class OriginalMaxHitPoint {
	final int value;
    OriginalMaxHitPoint(final int value) {
   		validate(value);
        this.value = value;
    }
}

class CorrectedMaxHitPoint {
	final int value;
    CorrectedMaxHitPoint(final OriginalMaxHitPoint originalMaxHitPoint,
					    final Accessory accessory,
					    final Armor armor) {
        this.value = originalMaxHitPoint.value + accessory.maxHitPointIncrements() + armor.maxHitPointIncrements();
    }
}

의미가 다른 개념들을 서로 다른 값 클래스로 설계하면 개념 사이의 관계를 이해하기 쉬워진다.
개념에 수식어를 붙여서 차이를 나타낸다면 각각을 클래스로 설계할 수 없는지 검토해보자.

  • int maxHitPoint -> int originalMaxHitPoint, int correctedMaxHitPoint -> OriginalMaxHitPoint.class, CorrectedMaxHitPoint.class

0개의 댓글