Skip to content

Latest commit

 

History

History
438 lines (372 loc) · 22.4 KB

02 의미있는 이름.md

File metadata and controls

438 lines (372 loc) · 22.4 KB

들어가면서

소프트웨어에서 이름은 모든 분야를 막론하고 사용하므로 이름을 잘 지으면 여러모로 편리할 것 입니다.

목차

1. 의도를 분명히 밝혀라
2. 그릇된 정보를 피하라
3. 의미있게 구분하라
4. 발음하기 쉬운 이름을 사용하라
5. 검색하기 쉬운 이름을 사용하라
6. 자신의 기억력을 자랑하지 마라
7. 클래스와 메서드의 이름
8. 기발한 이름을 피하라
9. 한 개념에 한 단어를 사용하라 10. 말장난을 하지 마라
11. 해법 영역에서 가져온 이름을 사용하라
12. 문제 영역에서 가져온 이름을 사용하라
13. 의미 있는 맥락을 추가하라
14. 불필요한 맥락을 없애라
15. 마치며

의도를 분명히 밝혀라

좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많습니다.

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

해당 질문에 대답을 하지 못하는 이름은 의도를 분명히 드러내지 못한 이름입니다.

잘못된 코드

int et; // 경과 시간(단위: 날짜)
int dc;
int dm;
int fa;

개선 코드

int elapsedTimeInDays;   
int daysSinceCreation;    
int daysSinceModification;    
int fileAgeInDays;   

의도가 드러나는 이름을 사용하면 코드 이해와 변경이 쉬워집니다.


잘못된 코드

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

이름만 명학하게 정의해줌으로써 코드의 단순성은 변하지 않고 코드는 더욱 명확해집니다.


한 걸음 더 나아가, int 배열을 사용하는 대신 간단한 클래스로 만들어도 될 것입니다.
isFlagged라는 좀 더 명시적인 함수를 사용해 FLAGGED 라는 상수를 감춰도 좋습니다.

조금 더 개선한 코드

class Cell{
    private String status;

    public boolean isFlagged(){
        if(status == "FLAGGED") return true;
        return false; // else 는 굳이 사용하지 않아도 됩니다.   
    }
}
    public List<Cell> getFlaggedCells(){ // 깃발 셀들을 얻겠다.
        List<Cell> flaggedCells = new ArrayList<Cell>(); // 깃발 셀들
        for(Cell cell : gameBoard){ // 게임보드에 들어있는 각각의 셀 꺼낸다.
            if(cell.isFlagged()){ // 상태 값이 == 깃발이라면
                flaggedCells.add(cell); // 추가한다.
            }
        }
        return flaggedCells;
    }
  • List<int []> -> List<Cell>
  • 리터럴 타입들을 클래스로 만들고 명확한 이름을 부여한다면 보다 명시적인 코드를 작성할 수 있습니다.
    • 리터럴 타입들 같은 경우 매개변수, 반환형으로 사용 뿐만 아니라 일반적으로 코드의 맥락을 알기 쉽지 않습니다.

그릇된 정보를 피하라

프로그래머는 코드에 그릇된 단서를 남겨서는 안 됩니다.

  • 실제 List가 아니라면 List라는 이름을 붙이지 않습니다.
    • 계정을 담는 컨테이너가 실제 List 형태가 아니라면 프로그래머에게 그릇된 정보를 제공하는 셈입니다.
    • List 형태라도 컨테이너 유형(컬렉션,자료구조)을 이름에 넣지 않는 편이 바람직합니다.
    • 단순히 Accounts, accountGroup, bunchOfAccounts라 명명하는 것이 좋습니다.
  • 서로 흡사한 이름을 사용하지 않도록 주의합니다.
    • 예시 : XYZControllerForEfficientHandlinfOfStrings 와 XYZControllerForEfficientStorageOfStrings 이름 사용
    • 가독성도 떨어지고 오히려 작성시 헷갈림으로써 코드의 오류를 야기할 수 있다.
  • 나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용하면 안 됩니다.
    • 예를 들면 hp, aix, soc 와 같이 유닉스에서 사용하는 이름은 변수 이름으로 적합하지 않습니다.
    • 이렇듯 우리의 생활속에서 이미 다른 의미로 사용되어 있는 이름을 사용하는 것은 그다지 권장하지 않는다.
  • 소문자 Ll로써 숫자 1처럼 보이고 대문자 OO로써 숫자 0처럼 보이기에 사용을 권장하지 않습니다.

의미있게 구분하라

컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 프로그래머는 스스로 문제를 일으킵니다.

  • 이름이 달라야 한다면 의미도 달라져야 한다.
    • 연속적인 숫자를 덧붙인 이름 (value1, value2....)은 의도적인 이름과 정반대이므로 사용을 권장하지 않습니다.
      • 그릇된 정보를 제공하는 이름도 아니며, 그저 아무런 정보를 제공하지 못하는 이름일 뿐입니다.
      • 저자의 의도가 전혀 드러나지 않는 이름입니다.

나쁜 코드

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

개선 코드

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

이름을 지정해줌으로써 코드 읽기가 훨씬 더 간단해졌습니다.


  • 불용어는 중복이기에 불용어를 추가한 이름 역시 아무런 정보도 제공하지 못합니다.
    • 접두어를 사용하지 말라는 의미가 아닙니다.
    • class 변수가 있단 이유로 klass라는 이름을 지어서는 안된다는 말입니다.
    • 변수 이름에 variable, 테이블 이름에 table 이란 단어를 붙이는 것은 필요가 없는 것입니다.
      • Customer 클래스와 CustomerObject 클래스의 차이를 알 수 있겠습니까?
    • 그렇기에 읽는 사람이 차이를 알도록 이름을 지어주도록 해야합니다

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

사람들은 단어에 능숙합니다.
우리 두뇌에서 상당 부분은 단어라는 개념만 전적으로 처리하기에 발음하기 쉬운 이름을 선택하는 것이 좋습니다.

나쁜 코드

class DatRcrd02{
    private Date genymdhms;
    private Date modymdhms;
    private String pszqint = "102";
}

개선 코드

class Customer{
    private Date GenerationTimestamp;
    private Date ModificationTimestamp;
    private final String recordId="102";
}
  • 이렇게 개선한 것이 훨씬 가독성과 이해가 쉬우며 무엇보다 지적인 대화가 가능해집니다.
    • 마이키, 이 레코드 좀 보세요. GenerationTimestamp 값이 내일 날짜입니다! 이렇게 말이죠

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

  • 문자 하나만 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다는 문제점이 있다.
  • 이런 관점에서 짧은 이름보다 긴 이름이 좋으며 검색하기 쉬운 이름이 상수보다 좋다.
  • 이름 길이는 해당 데이터의 사용 범위 크기에 비례해야 한다
    • 변수나 상수를 코드 여러 곳에서 사용한다면 즉, 범위가 큰 것이라면 검색하기 쉬운 이름이 바람직하다.

나쁜 코드

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

개선 코드

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

이름을 의미 있게 지으면 함수가 길어지지만 WORK_DAYS_PER_WEEK 와 같이 찾기가 쉬워집니다.

인코딩을 피하라

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

헝가리식 표기법

  • 자바는 프로그래머는 자료형이 명시적으로 드러나기에 이름에 타입을 인코딩할 필요가 없다.
    • 따라서 헝가리식 표기법이나 기타 인코딩 방식이 오히려 방해가 될 뿐이다.
    • 변수, 함수, 클래스 이름이나 타입을 바꾸기가 어려워지며, 읽기도 어려워진다.
    • PhoneNumber phoneString;와 같이 타입이 바뀌어도 이름은 바뀌지 않는다면 문제가 생긴다.

맴버 변수 접두어

  • 멤버 변수에 m_이라는 접두어를 붙일 필요도 없습니다.
    • 클래스와 함수는 접두어가 필요없을 정도로 작아야 마땅합니다.
    • 또한 멤버 변수를 다른 색상으로 표시하거나 눈에 띄게 보여주는 IDE를 사용해야 마땅합니다.

나쁜 코드이자 옛날 코드

class Part{
    private String m_dsc;
    void setName(String name){
        m_dsc = name;
    }
}

개선 코드

class Part{
    private String description ;
    void setName(String description){
        this.description = description;
    }
}

인터페이스 클래스와 구현 클래스

인터페이스 클래스와 구현을 위한 구체 클래스의 이름을 어떻게 지어야 좋을까?

  • 인터페이스 이름은 접두어를 붙이지 않는 편이 좋습니다.
    • 접두어는 주의를 흐트리고 과도한 정보를 제공해주기 때문이다.
    • 필자 생각 : 작가가 맞다고 해서 인정하는 것이 아니라 인터페이스는 결국 다형성을 위한 것이므로 큰 주제를 이름으로 지정하면 될 것입니다.
  • 인터페이스 클래스 이름과 구현 클래스 이름 중 하나를 인코딩해야 한다면 구현 클래스 이름을 택하는 것이 좋습니다.
    • ShapeFactoryImplIShapeFactory(Interface의 I)보다 좋습니다.
  • 인터페이스 변수 이름은 인터페이스라고 명시하지 않고 그저 일반적으로 사용하는게 낫다는 의미입니다.

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

변수 이름을 자신만 아는 이름으로 변환한다면 그 변수 이름은 바람직하지 못합니다.
예를 들면 반복문에 사용하는 변수 i,j,k(l 제외) 를 제외하고 문자 하나만 사용하는 변수 이름은 문제가 있습니다.

똑똑한 프로그래머와 전문가 프로그래머 사이에서 나타는 차이점 하나만 들자면,
전문가 프로그래머는 명료함이 최고라는 사실을 이해하여 자신의 능력을 좋은 방향을 사용해 남들이 이해하는 하는 코드를 내놓습니다.

클래스와 메서드의 이름

  • 클래스 이름과 객체 이름은 명사가 적합합니다.
    • Customer, WikiPage, Account, AddressParser 등이 좋은 예
    • Manager, Processor, Data, Info 등과 같은 단어는 피하고 동사는 사용하지 않는것이 좋습니다.
  • 메서드 이름은 동사나 동사구가 적합합니다.
    • postPayment, deletePage, save 등이 좋은 예
    • 접근자, 변경자, 조건자는 JavaBean 표준에 따라 값 앞에 get, set, is를 붙입니다.
  • 생성자를 중복 정의할 때는 static 팩토리 메서드를 사용합니다.
    • 참고로 생성자 사용을 제한하려면 해당 생성자를 private로 선언합니다.
  • 메서드는 인수를 설명하는 이름을 사용합니다.
    String name = employee.getName();
    customer.setName("mike");
    if (paycheck.isPosted())...

나쁜 코드

    Complex fulcrumPoint = new Complex(23.0);

개선 코드

    Complex fulcrumPoint = new Complex.FromRealNumber(23.0);

위 코드처럼 클래스와 메서드에 알맞는 이름을 사용해야
내가 주입하려는 값이 어떠한 형태로 동작하는지 알 수 있으며 검증도 할 수 있습니다.

기발한 이름을 피하라

이름이 너무 기발하면 저자와 유머감각이 비슷한 사람만, 그리고 농담을 기억하는 동안만, 이름을 기억한다.

  • 재미난 이름보다 명료한 이름을 선택하라
  • 또한 특정 문화에서만 사용하는 농담은 피하는 편이 좋다.
  • 의도를 분명히 하고 솔직하게 표현하도록 하자

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

  • 추상적인 개념 하나에 단어 하나를 선택해 이를 고수하는 것이 좋습니다.
    • 예를 들어, 똑같은 메서드를 클래스마다 다른 이름으로 부르면 혼란스러우며 이를 기억하는 것은 쉽지 않습니다.
      • 예를 들면 go, run 이런식으로 사용하면 헷갈립니다.
    • 메서드 이름은 독자적이고 일관적이어야 해당 메서드의 주석을 뒤져보지 않고도 프로그래머가 올바른 메서드를 선택할 수 있습니다.
    • 클래스에서도 마찬가지이다.
      • 기능은 같지만 어느 클래스는 Controller 를 사용하고 다른 클래스는 Manage를 쓴다면 이는 앞선 문제와 똑같이 직면합니다.

말장난을 하지 마라

  • 한 단어를 두 가지 목적으로 사용하지 않는것이 좋습니다.
    • 다른 개념에 같은 단어를 사용한다면 그것은 말장난에 불과할 뿐입니다.
      • 만약 '한 개념에 한 단어를 사용하라'는 규칙을 따랐더니, 여러 클래스에 add라는 메서드가 생겼다면?
      • 모든 add 메서드의 매개변수와 반환값이 의미적으로 똑같다면 문제가 없습니다.
    • 때로는 프로그래머가 같은 맥락이 아닌데도 '일관성'을 고려해 add라는 단어를 선택하는데 이는 좋지 않습니다.
    • 메서드의 맥락이 다르면 차라리 insert나 append라는 이름이 적당합니다.

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

  • 코드를 읽을 사람도 프로그래머라는 사실을 명심합시다.
    • 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮습니다.
    • 모든 이름을 문제 영역(도메인 영역)에서 가져오는 정책은 현명하지 못합니다.
      • 같은 개념을 다른 이름으로 이해하던 동료들이 매번 고객에게 의미를 물어야하기 때문입니다.
  • 기술 개념에는 기술 이름이 가장 적합한 선택입니다.

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

  • 적절한 '프로그래머 용어'가 없다면 문제 영역에서 이름을 가져옵니다.
    • 코드를 보수하는 프로그래머가 전문가에게 의미를 물어 파악할 수 있습니다.
  • 문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 합니다.

의미 있는 맥락을 추가하라

함수를 기준으로 함수 이름은 맥락 일부만 제공하며, 알고리즘이 나머지 맥락을 제공한다.

  • 함수는 한가지 기능만 한도록 설계해야합니다.
  • 공통된 부분은 외부로 빼내고 한가지 맥락의 기능만 수행하도록 해야합니다.
  • 공통된 부분이 밀접한 관련이 있다면 클래스로 만들어 관리하는 '의미 있는 맥락'을 추가시키는 것도 좋습니다.

나쁜 코드

    private void printGuessStatics(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(geuessMessage);
    }

개선 코드

public class GuessStaticsMessage {
    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 = "s";
    }

    private void thereIsOneLetter() {
        number = "1";
        verb = "is";
        pluralModifier = "";
    }

    private void thereAreNoLetters() {
        number = "no";
        verb = "are";
        pluralModifier = "s";
    }
    
}
  • 그러면 세 변수는 맥락이 분명해졌습니다. (세 변수는 GuessStaticsMessage에 속한다.)
  • 이렇게 맥락을 개선하면 함수를 쪼개기가 쉬워지므로 알고리즘도 좀 더 명확해집니다.

불필요한 맥락을 없애라

'고급 휘발유 충전소(Gas Station Deluxe)' 라는 애플리케이션을 짠다고 가정했을시
모든 클래스 이름을 GSD로 시작하겠다는 생각은 전혀 바람직하지 못하다.
이는 이름에 중복된 글자가 포함되기 때문이다.

  • 명사를 기반으로 이름을 짓는다면 실 세계에서 붙인 이름 그대로 사용하는 것이 좋습니다.
    • accountAddress 같은 이름은 Address 클래스의 변수 이름으로 사용되는 것이 좋을 것 같습니다.
    • 포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, MAC, URI라는 이름도 괜찮을 것입니다.

마치며

우리들 대다수는 자신이 짠 클래스 이름과 메서드 이름을 모두 암기하지 못합니다.
암기는 요즘 나오는 도구에게 맡기고,
우리는 문장이나 문단처럼 읽히는 코드 아니면 적어도 표나 자료 구조처럼 읽히는 코드를 짜는데만 집중해야 마땅합니다.
이러한 과정에서 좋은 이름을 선택하여 보다 직관적이고 명확하게 코드를 이해할 수 있도록 도와주어야 합니다.
단기적인 효과는 물론 장기적인 이익도 보장해 줄 것입니다.

간단 정리

  1. 의도를 분명히 밝혀라 : 이름만 보고도 무엇을 나타내는지 명확해야 한다.
  2. 그릇된 정보를 피하라 : 유사하다고 통합적으로 사용하지 말고 일치되는 이름만 사용하자.
  3. 의미있게 구분하라 : 컴파일만 통과되기 위한게 아니라 '명확한 이름'을 붙여줘라
  4. 발음하기 쉬운 이름을 사용하라 : 이름은 단순한게 좋다
  5. 검색하기 쉬운 이름을 사용하라 : 넓은 범위로 자주 사용되는 경우 적당히 긴 이름이 좋습니다.(검색에는 긴게 좋음)
  6. 자신의 기억력을 자랑하지 말아라 : 나만 기억하는 이름을 사용하지 않는 것이 좋습니다.
  7. 클래스와 메서드의 이름 : 클래스-명사, 메서드-동사
  8. 기발한 이름은 피하라 : 단어는 명확한 것이 제일 좋다.(괜한 센스 발휘하지 말자)
  9. 한 개념에 한 단어를 사용하라 : 여러 단어로 해석되면 혼란만 야기하므로 명확한 단어만 사용하자
  10. 말장난을 하지 마라 : 단어는 명확한 것이 제일 좋다.(괜한 센스 발휘하지 말자)
  11. 해법 영역에서 가져온 이름을 사용하라 : 코드를 볼 사람도 결국 개발자이니 개발자 용어를 사용해도 된다.
  12. 문제 영역에서 가져온 이름을 사용하라 : 문제 영역의 이름을 사용할 경우 단어를 몰라도 해당 관련자에게 물어봐서 예상할 수 있습니다.
  13. 의미 있는 맥락을 추가하라 : 함수의 공통된 부분이 너무 밀접할 경우 클래스로 만드는 것도 좋다.
  14. 불필요한 맥락을 없애라 : 명사일 경우 기존 지어준 이름을 그대로 사용하는 것이 좋다.