제육's 휘발성 코딩
반응형

나쁜 코드

  • 성능이 나쁜 코드
    • 불필요한 연산이 들어가서 개선의 여지가 있는 코드
  • 의미가 모호한 코드
    • 이해하기 어려운 코드, 네이밍과 그 내용이 다른 코드
  • 중복된 코드
    • 비슷한 내용인데 중복되는 코드들은 버그를 낳는다.
  • 깨진 유리창 법칙 - 나쁜 코드는 유리창 처럼 계속 나쁜 코드가 만들어지도록 한다.
  • 생산성 저하 - 나쁜 코드는 팀 생산성을 저하시킨다.
    • 기술부채를 만들어 수정을 더 어렵게 한다.
    • 결국 새로운 시스템을 만들어야 한다.

시간이 없다고 나쁜 코드를 사용하면, 결국 일정을 못맞춘다.


클린 코드

  • 성능이 좋은 코드
  • 의미가 명확한 코드 - 가독성이 좋은 코드
  • 중복이 제거된 코드
  • 보이스카우트 룰 : 전보다 더 깨끗한 상태로 만든다.

의미가 분명한 이름 짓기

  • 이름을 잘 짓는 간단한 규칙
  • 소프트웨어에서 이름은 어디서나 쓰인다.

의도를 분명히 밝혀라

int d; // 경과 시간(날짜)

int daysSinceCreation;

이름 d는 아무 의미도 드러나지 않는다. 측정하려는 값과 단위를 표현하는 이름이 필요하다.

public List<int[]> getThem(){
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList)
        if (x[0] == 4)
            list.add(x);
    return list1;
}

변수는 세개, 상수는 두 개뿐이지만, 코드의 단순성이 아닌 코드의 함축성에 문제가 있다.

  1. theList에 무엇이 들었는가?
  2. theList에서 0번째 값이 어째서 중요한가?
  3. 값 4는 무슨 의미 인가?
  4. 함수가 반환하는 list1을 어떻게 사용하는가?
public List<int[]> getFlaggedCells(){
    List<int[]> flaggedCells = new ArrayList<>();
    for (int[] cell : gameBoard)
        if (cell[STATUS_VALUE] == FLAGGED)
            flaggedCells.add(cell);
    return flaggedCells;
}

코드의 단순성은 변하지 않으면서 코드는 명확해졌다. (지뢰찾기 게임 예시)

public List<Cell> getFlaggedCells(){
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : gameBoard)
        if (cell.isFlagged())
            flaggedCells.add(cell);
    return flaggedCells;
}

int 배열 대신 Cell 클래스를 만들어서 사용하고, isFlagged() 메서드를 만들어서 FLAGGED라는 상수를

감춰서 좀 더 개선할 수 있다.

단순 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다.

변수명에 타입 넣지 않기

사진2

List, Map 의 경우는 변수명을 사용하기도 한다.

구현체에 Impl 네이밍하는 방법은 아직까지 사용중이기도 하다.

의미 있게 구분하라

  • 연속된 숫자를 덧붙이거나 불용어(noise word)를 추가하는 방식은 부적절하다.
  • a1, a2, ... , aN은 의도적인 이름과 정반대다.
    • 저자 의도가 전혀 드러나지 않는다.
public static void copyChars(char a1[], char a2[]){
    for (int i=0; i<a1.length; i++){
        a2[i] = a1[i]
    }
}

함수 인수 이름으로 불용어를 추가한 예시

  • 불용어는 중복이다.
  • getActiveAccount(); , getActivateAccounts(); 와 같은 메서드가 있다면 읽는 사람의 입장에선 차이를 알기 어렵다. 차이를 알기 쉽게 이름을 지어라.

발음하기 쉬운 이름을 사용하라

private Date genymdhms; vs private Date generationTimestamp;

당연히 두 번째 이름이 옳다. 그래야지만 의사소통이 가능해진다.

자신의 기억력을 자랑하지 마라

  • 루프에서 반복 횟수를 세는 변수 i, j, k는 괜찮다. (l은 절대 안된다. 숫자1과 헷갈리기도..)
  • 한 단어를 두가지 목적으로 사용하지 말자.
    • add 메서드가 생성된 후, 추가 메서드가 같은 맥락이 아니라면 insert, append 와 같은 네이밍 사용

클래스 이름

  • 클래스 이름과 객체 이름은 명사나 명사구가 적합
  • Customer, WikiPage, Account, AddressParser --- 옳은 네이밍
  • 동사는 사용하지 않는다.

메서드 이름

  • 메서드 이름은 동사나 동사구가 적합
  • postPayment, deletePage, save --- 옳은 네이밍
  • 접근자, 변경자, 조건자는 javabean 표준에 따라 get, set, is를 붙인다.
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
Complex fulcrumPoint = new Complex(23.0);

아래 코드가 위코드 보다 좋은 코드이다.

생성자를 중복정의 할 때는 정적 팩토리 메서드를 사용한다.

생성자를 제한하려면 해당 생성자를 private 으로 막는다. (싱글톤)

의미 있는 맥락을 추가하라

  • 스스로 의미가 분명한 이름이 없지 않지만, 대다수 이름은 그렇지 못한 경우가 많다.
  • 클래스, 함수, 이름 공간에 넣어 맥락을 부여하고, 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.
    • firstName, street, city, state 를 개별적으로 봤을 땐 주소라는 사실을 알기 어렵다.
    • addrFirstName, addrLastName 과 같이 분명한 네이밍을 하자.
    • Address 클래스를 생성하는 방법도 존재한다.
private void printGuessStatistics(char candidate, int count) {
    String number;
    String verb;
    String pluraModifier;
    if (count==0){
        number = "no";
        verb = "are";
        pluralModifier ="s";
    }
    else if (count==1){
        number = "1";
        verb = "is";
        pluralModifier="";
    }
    ...
    String guessMessage = String.format(
    "There %s %s %s %s", verb, number, candidate, pluraModifier);
    print(guessMessage);
}

해당 메서드는 맥락이 불분명하다. 변수의 의미를 파악하기 위해 독자가 맥락을 유추해야만 한다.

그냥 메서드만 훓어서는 세 변수의 의미가 불분명하다.

public class GuessStatisticMessage {
    private String number;
    private String verb;
    private String pluralModifier;

    public String make(char candidate, int count){
        createPluralDependentMessageParsts(count);
        return String.format(
        "There %s %s %s %s", verb, number, candidate, pluraModifier);
    }

    private void createPluralDependentMessageParsts(int count){
        if (count == 0){
            thereAreNoLetters();
        } 
        ... 이하 생략 
    }

    private void thereAreNoLetters(){
        number = "no";
        verb = "are";
        pluralModifier = "s";
    }
}

함수를 작은 조각으로 쪼개고자 GuessStatisticMessage라는 클래스를 만든 후 세 변수를 클래스에 넣었다.

맥락이 분명해졌으며, 함수를 쪼개기가 쉬워지므로 알고리즘도 자동으로 더 명확해진다.

구글 자바 네이밍 가이드

Package Naming Guide

  • 모두 소문자로 사용하며, _를 사용하지 않는다.

Class Namging Guide

  • 대문자로 시작하는 카멜 케이스 사용
  • 클래스는 명사, 명사구
    • Character, ImmutableList
  • 인터페이스는 명사, 명사구, (형용사도 가능)
    • List, Readable
  • 테스트 클래스는 Test로 끝나기
    • HashTest, HashIntegrationTest

Method Naming Guide

  • 소문자로 시작하는 카멜 케이스
  • 메서드는 동사, 동사구
    • sendMessage, stop
  • junit 테스트에 underscore 사용되기도 한다.
    • pop_emptyStack

본 포스팅은 Clean Code (Robert C. Martin), 제로베이스-한달한권(클린코드)를 참고하여 작성하였습니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^