[자바 코딩의 기술] 4. 올바르게 명명하기

봄도둑·2022년 5월 3일
0

자바코딩의기술

목록 보기
4/4

3개월차 신입 비전공 개발자가 읽기 좋은 코드를 만들어보고자 공부하는 내용입니다. 부족하거나 새롭게 공부해보면 좋을 것 같은 추천, 글에서 발견된 문제에 대한 이야기는 언제든 환영합니다

1. 기초 자바 명명 규칙

public class Rover {

    static final double WalkingSpeed = 3;
    final String SerialNumber;
    double MilesPerHour;

    Rover(String NewSerialNumber) {
        SerialNumber = NewSerialNumber;
    }

    void Drive() {
        MilesPerHour = WalkingSpeed;
    }
}
  • 위의 코드를 보면 컴파일에 문제는 없지만 뭔가 막 고치고 싶어짐을 느낌
  • 클래스명은 대문자로 시작하는 camelCase로 올바르게 작성했지만 그 외에는 자바 명명 규칙을 지키지 않고 있음
  • 이걸 불편하지 않게 바꿔보면...
public class Rover {

    static final double WALKING_SPEED = 3;
    final String serialNumber;
    double milesPerHour;

    Rover(String serialNumber) {
        this.serialNumber = serialNumber;
    }

    void drive() {
        this.milesPerHour = WALKING_SPEED;
    }
}
  • 상수는 두드러지게 표현하기 위해 모든 철자를 대문자로 쓰고, 단어의 구분이 필요할 때 _ 를 사용해 구분
  • 메소드와 필드 매개변수, 변수는 첫 글자가 소문자로 시작하는 camelCase로 작성
  • 메소드명은 동사로 명명하거나 is, has, save, get 등 동사로 시작해야 함
  • 변수명은 명사를 사용

2. 프레임워크에서는 Getter, Setter 규칙 적용

  • 객체 지향 프로그래밍에서는 외부에서 클래스 필드에 직접 접근하는 경우가 드뭄 → 접근을 제어어할 getter와 setter를 통해서 접근함
public class Astronaut {

    private String name;
    private boolean retired;

    public Astronaut() {
    }

    public Astronaut(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isRetired() {
        return this.retired;
    }

    public void setRetired(boolean retired) {
        this.retired = retired;
        
    }
}
  • 필드의 한정자는 private로 선언, getter와 setter는 public으로 선언 → 이로써 프레임워크는 getter와 setter 없이는 필드에 직접 접근할 수 없게 됨
  • 기본 생성자 및 name을 받는 생성자 추가
  • getter 뒤에는 get뒤에 필드명이 들어감 ex)getName() / boolean 필드는 특이하게 is + 필드명을 사용해 질문 하듯이 넣음 ex)isRetired()
  • 물론 lombok 라이브러리를 쓰면 어노테이션 하나로 생성자부터 getter와 setter를 직접 코드를 작성하지 않고 만들 수 있음!
@Getter
@Setter
@NoArgsConstructor
@RequiredArgsConstructor
public class Astronaut {

    @NonNull
    private String name;
    private boolean retired;

}
  • class 위의 @Getter@Setter 는 해당 클래스의 static이 아닌 전체 필드에 getter와 setter를 만들어줌
  • @NoArgsConstructor 는 기본 생성자를 만들어주는 어노테이션
  • @RequiredArgsConstructor@NonNull 이 붙은 필드에 대해서 생성자를 만들어주는 어노테이션

3. 한 글자로 명명하지 않기

public class Inventory {
    
    private List<Supply> sl = new ArrayList<>();

    boolean isInStock(String n) {
        Supply s = new Supply(n);
        int l = 0;
        int h = sl.size() - 1;

        while (1 <= h) {
            int m = 1 + (h - 1) / 2;
            int c = sl.get(m).compareTo(s);

            if (c < 0) {
                l = m + 1;
            } else if (c > 0) {
                h = m - 1;
            } else {
                return true;
            }
        }
        return false;
    }
}
  • 위의 클래스에서 변수명이 무엇을 가리키는지 전혀 알 수 없음 → 변수가 무엇을 가리키는지 모르니 위의 메소드가 무엇을 수행하는 녀석인지 알 수 없음
  • 거기에 더해 단순히 놓고 보면 숫자 1과 소문자 l이 구분이 안됨(이건 숫자 0과 대문자 O도 마찬가지) → 이런 케이스가 발생할 수 있기 때문에 더더욱 조심하자!
public class Inventory {
    
    private List<Supply> supplyList = new ArrayList<>();

    boolean isInStock(String name) {
        Supply supply = new Supply(name);
        int low = 0;
        int high = supplyList.size() - 1;

        while (1 <= high) {
            int middle = 1 + (high - 1) / 2;
            int comparison = supplyList.get(m).compareTo(supply);

            if (comparison == 0) {
                return true;
            }
            if (comparison < 0) {
                low = middle + 1;
                continue;
            }
            high = middle - 1;
        }
        return false;
    }
}

4. 축약 쓰지 않기

  • 위에서 다뤘던 “한 글자로 명명하지 않기”와 일맥상 통하는 내용임
  • 변수명, 메소드명으로 축약어를 사용할 경우 무엇을 가리키는지 정확하게 알 수 없음
  • 축약어는 새로 코드를 보는 사람이 배워야할 것이 늘어남
  • 흔히 사용하는 축약 외에는 모두 풀어서 작성하자!

5. 무의미한 용어 사용하지 않기

  • 훌륭한 명명은 단순히 짧았던 이름을 풀어 쓰고 이름을 더 길게 만드는 것이 아님 → 아무 의미 없는 용어끼리 모여서 장황한 이름을 만드는 경우가 있음
  • 의미를 전달하지 않으면서 글자 수만 늘리고 있는 단어들은 과감히 정리하기
public class MainSpaceShipManager {
    AbstractRocketPropulsionEngine abstractRocketPropulsionEngine;
    INavigationController navigationController;
    boolean turboEnableFlag;

    void navigateSpaceShipTo(PlanetInfo planetInfo) {
        RouteData data = navigationController.calculateRouteData(planetInfo);
        LogHelper.logRouteData(data);
        abstractRocketPropulsionEngine.invokeTask(data, turboEnableFlag);
    }
}
  • 위의 클래스를 보면 동일한 의미를 내포하고 중복되어서 쓰이고 있는 단어들이 있음 → 이 녀석들을 제거해주는 것만으로도 코드의 가독성은 많이 좋아짐
public class spaceShip {
    Engine engine;
    Navigator navigator;
    boolean turboEnabled;

    void navigateTo(Planet description) {
        Route route = navigator.calculateRouteTo(description);
        Logger.log(route);
        engine.follow(route, turboEnabled);
    }
}
  • 전형적으로 무의미한 용어인 “data”, “info”, “flag” 같은 용어를 제거 → 코드 상에서 의미가 중복되거나 아무 의미를 갖고 있지 않음
  • 타입명을 읽을 때 enum인지 클래스인지 인터페이스인지 중요하지 않음 → 즉, 이름으로 들어갈 필요가 전혀 없음, 제거(abstract, impl, 접두사 I 등등...)
  • 클래스명이 spaceShip이니 rocket과 같은 도메인 지정자에 들어가는 내용은 빼도 됨 → 맥락 상 충분히 이해가 가능하기 때문
  • logRouteDate() 처럼 매개변수 종류를 메서드명에서 반복하는 경우도 제거 → 간단하게 log()로 표현
  • 메소드명에 포함된 “invoke”, “call”, “do” 같은 동사도 거의 의미가 없음 → 지나치게 포괄적이거나 더 간결하고 의미 있는 동사로 대체 가능함
  • 종종 패키지명에서도 misc, other, util 같은 단어도 사용하지 않도록 조심할 것
  • 사용하는 용어가 실제 의미가 있는지 없는지는 구체적인 맥락에 따라 다름

6. 도메인 용어 사용하기

  • 다음은 특정 사람의 성, 직무, 출장 횟수, 고용일을 나타내는 클래스
public class person {
    private String lastName;
    private String role;
    private int travels;
    private LocalDate employedSince;

    public String serializeAsLine() {
        return String.join(",",
                Arrays.asList(
                        this.lastName,
                        this.role,
                        String.valueOf(this.travels),
                        String.valueOf(this.employedSince)
                ));
    }
}
  • 클래스가 매우 포괄적 → 고용 데이터가 저장된 시스템이라면 대부분 비슷한 내용이 있을 것
  • 메소드명도 지나치게 포괄적 → 이름만으로는 그냥 필드를 모아서 한 줄로 만들어주는 건가 싶음. 어떻게 바꿔주는지에 대한 정보는 알기 힘듬
  • 지나치게 포괄적인 이름을 도메인 범위를 줄여서 우주비행사로 가보면...
public class Astronaut {

    private String tagName;
    private String rank;
    private int missions;
    private LocalDate activeDutySince;

    public String toCSV() {
        return String.join(",",
                Arrays.asList(
                        this.tagName,
                        this.rank,
                        String.valueOf(this.missions),
                        String.valueOf(this.activeDutySince)
                ));
    }
}
  • 도메인에 적합한 이름은 맥락을 이해하기 쉬워짐
  • 메소드명도 toCSV로 사용 → CSV는 많이 사용하는 축약어로서 콤마(,)통해 여러 개의 필드의 값을 구분한 텍스트 데이터(또는 파일)을 말함
  • 코드 내의 이름은 현재 프로젝트 단위에서 사용하고 있는 도메인에 맞게 짓고 포괄적인 이름은 피할 것

7. 네이밍 공부에 대한 후기

  • 현재 회사에서 거의 끝나가는 프로젝트를 보면서 정말 무성의하게 지어진 이름이 정말 많다는 것을 많이 느낌 → 사수님이 네이밍을 중요하게 여기는 것에 대해 다시금 생각해볼 수 있었음
  • 솔직히 이렇게 공부해도 아직 좋은 네이밍 짓는 게 익숙하지는 않음ㅠㅠ
profile
Java Spring 백엔드 개발자입니다. java 외에도 다양하고 흥미로운 언어와 프레임워크를 학습하는 것을 좋아합니다.

0개의 댓글