의도를 밝혀라

변수, 함수, 클래스의 이름은 

존재 이유? 수행 기능? 사용 방법?

이 질문에 대해 답할 수 있어야 한다

 

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

 

간단하지만 무슨 동작을 하는지 짐작하기 어려운 코드의 예시

 

theList 에 무엇이 들어있는지

theList 의 0 번째 값이 왜 중요한지

값 4는 무슨 의미인지

함수가 반환하는 list1 을 어떻게 사용하는지

 

위에 대한 정보가 없다

 

public List<int[]> getFlaggedCells(){
	List<int[]> flaggedCells = new ArrayList<int[]>;
    for(int[] cell : gameBoard)
    	if(cell[STATUS_VALUE]==FLAGGED)
        	flaggedCells.add(cell);
     return flaggedCells;
}

 

지뢰찾기 게임

gameBoard : 전체 게임 판

cell : 각 칸

STATUS_VALUE : 상태

FLAGGED : 깃발이 꽂힌 상태

 

이렇게 변경하면, 각각의 칸을 탐색하며 그 칸의 status 가 flagged 일 경우 flaggedCells 리스트에 넣는다, 즉, 깃발이 꽂힌 칸을 리턴하는 함수다 라는 것을 쉽게 파악할 수 있다

 

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

 

int[] 을 Cell 자료형으로 새롭게 표혀한 코드

매우 쉽게 코드를 이해할 수 있다

 

 

 

그릇된 정보를 피하라

프로그래머는 코드에 그릇된 정보를 남겨서는 안된다

예를 들어, 실제 리스트가 아닌 정보를 accountList (계정 모음) 등으로 표현하면 안된다

List 가 아님에도 변수명만 보고 List 라고 오해할 수 있기 때문

 

accountList >> accountGroup / bunchOfAccounts 등으로 표현하는 것이 적합

 

서로 흡사한 이름을 사용하지 않도록 주의하라

하나의 모듈에서 XYZControllerForEfficientHandlingOfStrings 라는 이름을 사용하고

다른 모듈에서 XYZControllerForEfficientHandlingOfStorageOfStrings 라는 이름을 사용한다면 

두 개가 헷갈릴 수 있다

 

유사한 개념은 유사한 표기법을 사용하라

왜? 일관성 유지를 위해서

 

 

int a = l;
if (O == l)
	a = O1;
else
	l = 01;

딱 봐도 O 와 0, 1과 l 가 헷갈리는 코드

매우 끔찍

 

 

의미있게 구분하라

public static void copyChar(char a1[], char a2[]) {
	for(int i = 0; i < a1.length; i++)
    	a2[i] = a1[i];
    }
}

위처럼 a1, a2, ... aN 등의 이름은 이름 그대로가 가지고 있는 의미가 없다

코드를 작성한 개발자의 의도가 전혀 드러나지 않는 코드 중 하나

 

 

함수 이름으로 source 와 destination 을 사용한다면 코드를 읽기 훨씬 편해진다

 

stopword 를 피하라

stopword 는 정보를 제공하지 않는 단어, 혹은 중복되는 단어

 

Info, Data 등의 접미사는 의미가 불분명한 용어

Product 라는 클래스가 있을 때, ProductInfo / ProductData 라는 클래스가 존재한다면 두 클래스의 개념이 모호해진다

 

MemberTable, 혹은 NameString 등의 변수명은 Member, Name 과 다른 의미를 가지지 못한다.

Cutomer 클래스와 CustomerObject 라는 클래스의 차이점 역시 찾기 힘들다

 

코드를 읽는 사람이 이름만 보고 차이를 알 수 있도록 이름을 지어야 한다

 

 

 

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

class DtaRcrd102{
    private Date genymdhms;
    private Date modymdhms;
    private final String pszqint = "102";
}
class Customer{
    private Date generationTimestamp;
    private Date modificationTimestamp;
    private final String recordId = "102";
}

두번째 코드는 원활한 대화가 가능하다

 

 

 

검색하기 쉬운 이름을 사용하라

MAX_CLASSED_PER_STUDENT 는 grep 으로 찾기 쉽지만, 숫자 '7'은 찾기 까다롭다

 

긴 이름이 짧은 이름보다 좋고, 검색하기 쉬운 이름이 상수보다 좋다

이름 길이는 범위 크기에 비례해야 한다

 

    for(int j = 0; j<34; j++){
        s += (t[j]*4)/5;
    }
    int realDaysPerIdalDay = 4;
    const int WORK_DAYS_PER_WEEK = 5;
    int sum = 0;

    for(int j = 0; j < NUMBER_OF_TASKS; j++){
        int realTaskDays = taskEstimate[j] * realDaysPerIdalDay;
        int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
        sum += realTaskWeeks;
    }

 

위 코드에서 sum 이 유용하진 않으나 검색이 가능하다

이름을 의미있게 지으면 함수가 길어진다

하지만, WORK_DAYS_PER_WEEK 를 찾기 쉽다

위처럼 상수 5를 사용한다면 검색하기 굉장히 어려워진다

 

 

인코딩을 피하라

유형, 범위 정보까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다

 

헝가리안 표기법은 기존 C언어 개발자들이 많이 사용

But 자바 프로그래머는 변수 이름에 타입을 인코딩할 필요가 없다

객체는 Strongly type 이며 IDE는 코드를 컴파일하지 않고도 타입 오류를 감지할 수 있다

 

 

그러나 때로는 인코딩이 필요한 경우도 있다

도형을 생성하는 Abstract Factory 의 Interface 가 존재하고 구체적인 구현은 Concrete class 에서 한다고 할 때, 

인터페이스는 ShapFactory, 구현 클래스는 ShapFactoryImpl  정도의 이름으로 정하는 것이 좋다

 

 

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

독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다

반복문에서의 변수 i, j, k 등을 제외하면 한 글자 이름은 대부분의 경우에 적절하지 못하다

 

 

 

클래스 이름

클래스 이름과 객체 이름은 명사, 명사구가 적합하다

 

Customer, WikiPage, Account, AddressParser 등이 좋은 예

Manager, Processor, Data, Info 등과 같은 단어는 좋지 않다

동사는 사용하지 않는 것이 좋다

 

 

메서드 이름

메서드 이름은 동사, 동사구가 적합하다

PostPayment, deletePage, save 등이 적절하다

Accessor, Mutator, Predicate 는 javabean표준에 따라 값 앞에 get, set, is 를 붙인다

String name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())...

 

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

메서드는 인수를 설명하는 이름을 사용

Complex fulcrumPoint = Complex.FromRealNumber(23.0);

이 코드가

Complex furcrumPoint = new Complex(23.0);

이 코드보다 좋다

 

생성자 사용을 제한하려면 해당 생성자를 private 로 선언한다

 

 

기발한 이름은 피해라

재미난 이름보다 명료한 이름을 선택해라

구어체, 속어를 이름으로 사용하는 사례가 있는데, 이는 좋지 못한 표현이다

 

Kill() 대신 whack()

Abort() 대신 eatMyShort() 등

특정 문화에서만 사용하는 농담이나 속어 등은 피해라

 

 

한 개념에 한 단어를 사용하라

추상적인 개념 하나에 단어 하나를 선택해 이를 고수하라

IDE 에서 객체를 사용하면 객체가 제공하는 메서드의 목록을 볼 수 있다

그러나 메서드 명, 매개변수 이름 정도를 보여주기 때문에 메서드 이름은 독자적이고 일관적이어야 한다

 

동일 코드 기반에 controller, manager, driver 등을 섞어 쓰면 혼란스럽다

 

 

 

말장난을 하지 마라

한 단어를 두 가지 목적으로 사용하지 마라

다른 개념에 같은 단어를 사용한다면 그건 말장난에 불과하다

 

 

예를 들어, 지금까지 구현한 add 메서드는 기존 값 두 개를 더하거나 이어서 새로운 값을 만드는 메서드였는데, 

집합 전체에 값 하나를 추가하는 메서드를 역시 add 라고 부르는 것은 맥락이 다르기 때문에 좋지 못하다

add 대신 insert, append 등이 적합하다

 

 

 

해법 영역(Solution Domain) 에서 가져온 이름을 사용하라

코드를 읽는 사람도 프로그래머라는 사실을 명심하라

전산용어, 알고리즘 이름, 패턴 이름, 수학적 용어 등을 사용해도 괜찮다

 

VISITOR 패턴에 익숙한 프로그래머는 AccountVisitor 라는 이름을 금방 이해한다

기술 개념에는 기술 이름이 가장 적합한 선택이다

 

 

문제 영역(Problem Domain) 에서 가져온 이름을 사용하라

적절한 프로그래머 용어가 없다면 문제 영역에서 이름을 가져온다

코드를 보수하는 프로그래머가 분야 전문가에게 의미를 물어 파악할 수 있다

 

 

 

의미 있는 맥락을 추가하라

firstName, lastName, street, houseNumber, city, state, zipcode 라는 변수가 있을 때, 주소라는 사실을 알아차리기 쉽다

하지만 어느 메서드가 state 변수 하나만 사용한다면, 그 때는 변수 state가 주소의 일부라는 사실을 알아차리기 어렵다

 

이 때, addr 이라는 접두어를 추가한다면 이름의 맥락이 분명해진다

addrFirstName, addrLastName, addrState 라고 쓴다면 주소의 일부를 나타낸다는 것을 쉽게 알아차릴 수 있다

(당연하게도 Address 라는 클래스를 생성하는 것이 더 좋다)

 

private void printGuessStatistics(char candidate, int count){
    String number;
    String verb;
    String pluralModifier;
    if (count==0){
        number = "no";
        verb = "are";
        pluralModifier = "s";
    }
    else if(count == 1){
        number = "1";
        verb = "is";
        pluralModifier = "";
    }
    else {
        number = Integer.toString(count);
        verb = "are";
        pluralModifier = "s";
    }
    
    String guessMessage = String.format("There %s %s %s%s", verb, number, candidate, pluralModifier);
    print(guessMessage);
}

위 코드에서 함수 이름은 부분적인 맥락 정보만 제공한다

따라서 알고리즘까지 이해해야 number, verb, pluralModifier 라는 변수들이 통계 추측 (guess statistics) 메세지에 사용된다는 것을 파악할 수 있다

 

public class GuessStatisticsMessage{
    
    private String number;
    private String verb;
    private String pluralModifier;
    
    public String make(char candidate, int count){
        createPluralDependentMessageParts(count);
        return String.format("There %s %s %s%s", verb, number, candidate, pluralModifier);
    }
    
    private void createPluralDependentMessageParts(int count){
        
        if(count==0){
            thereAreNoLetters();
        }
        else if(count == 1){
            thereIsOneLetter();
        }
        else{
            thereAreManyLetters(count);
        }
    }
    
    private void thereAreManyLetters(int count){
        number = Integer.toString(count);
        verb = "are";
        pluralModifier = "";
    }
    private void thereIsOneLetter(){
        number = "1";
        verb = "is";
        pluralModifier = "";
    }
    private void thereAreNoLetters(){
        number = "no";
        verb = "are";
        pluralModifier = "s";
    }
}

긴 함수

세 개의 변수를 함수 전반에 걸쳐 사용

두 가지를 해결하기 위해

GuessStatisticsMessage 라는 클래스를 만든 후 세 변수를 클래스의 필드로 선언하게 되면

number, verb, pluralModifier 세 변수는 확실하게 GuessStatisticsMessage 클래스에 속한다

즉, 함수 쪼개기가 쉽고 알고리즘도 명확해진다

 

 

 

불필요한 맥락을 없애라

예를 들어 Gas Station Deluxe 라는 애플리케이션을 만들 때, 모든 클래스 이름을 GSD 로 시작하는 것은 바람직하지 않다

 

accountAddress, customerAddress 는 Address 클래스 인스턴스로는 좋으나, 클래스 이름으로는 X

Address 는 클래스 이름으로 적합

Port, Mac, Web address 를 구분해야 한다면 PostalAddress, MAC, URI 라는 이름은 클래스 이름으로 괜찮다

 

 

 

 

'끄적 > Clean Code' 카테고리의 다른 글

Clean Code -6 오류 처리  (0) 2023.08.10
Clean Code -5 객체와 자료구조  (0) 2023.08.10
Clean Code -4 형식 맞추기  (0) 2023.08.10
Clean Code -3 주석  (0) 2023.08.10
Clean Code -2 함수  (0) 2023.08.09

+ Recent posts