Study Programming

변수명, 함수명, 클래스명 잘 짓는 방법 (Clean code: 2ch Naming)

네모메모 2023. 11. 20. 16:04
반응형

 

 

 

 

이름을 잘 짓는 규칙들


의도를 분명히 밝힌다. 

- 변수나 함수 드리고 클래스명은  변수(나 함수 드리고 클래스)의 존재 이유는? 수행 기능은? 사용 방법은? 이란 질문들에 답할 수 있어야 한다. 

- 주석이 필요하지 않는 수준이어야 함

   ㄴ> Bad example) int d; // 경과 시간

   ㄴㄴ> Good example) int elapsedTimeInDays;

 


그릇된 정보를 피하라 (= 이름에 그릇된 단서를 남기면 안된다.)

- 유사한 개념은 유사한 표기법을 사용한다. 

- 기발한 이름, 특정 유머나 문화권의 농담은 피하고 명료하게 표현하라.

- 이름에 다른 타입이 포함된 경우

   ㄴ> Bad example) List가 아닌 userList

   ㄴ> Bad example) 유닉스 플랫폼이나 변종이름인 hp, aix, sco라는 변수이름

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

   ㄴ> Bad example) MainControllerHandlingOfStrings, MainControllerStorageOfStrings

   ㄴ> Bad example) 변수명이 1처럼 보이는 l(소문자L)과 0처럼 보이는 O(대문자O)인 경우, a = l, o1 = 01;

 


 

의미있게 구분하라

- 동일한 범위 안에서는 다른 두 개념에 같은 이름을 사용하지 못한다. 때문에 한쪽 이름을 이상하게 바꾸고픈 유혹에 빠진다.

  ㄴ> Bad example) Class, Klass

- 이름이 달라야 한다면 의미도 달라져야 한다.

- 연속된 숫자를 추가하는 방식은 부적절하다.

  ㄴ> Bad example) 

fun copyChar(a1: List<Char>, a2: List<Char>) {
	for (i in 0 until a1.length) {
    	a2[i] = a1[i]
    }
}

  ㄴㄴ> Good example) 함수이름을 source, destination으로 변경

fun copyChar(source: List<Char>, destination: List<Char>) {
	for (i in 0 until source.length) {
    	destination[i] = source[i]
    }
}

 

- 불용어를 추가하는 방식은 부적절하다. 불용어는 중복이다. 읽는 사람이 차이를 알도록 명명해라.

  ㄴ> Bad example) Place, PlaceInfo, PlaceData

  ㄴ> Bad example) getActiveStudent(), getActiveStudents(), getActiveStudentInfo()

  ㄴ> Bad example) money, moneyAmount

- a, the 등의 접두어도 의미가 분명히 다르다면 사용가능

- 변수명에 variable이라는 단어는 단연코 금물이다. (표 이름의 table도 마찬가지)

 


 

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

  ㄴ> Bad example) genymdhms 

          - 단어 뜻 : generate date, year, month, day, hour, minute, second
          - 사람들 발음 : "젠 와이 엠 디 에이치 엠 에스" 혹은 "젠 샤 무다 힘즈"

   ㄴㄴ> Good example) generationTimestamp 로 변경한다면 대화가 훨씬 편해질 것이다.

 


 

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

- 검색하기 쉬운 이름이 상수보다 좋다.

  ㄴ> Bad example) 3 (상수)

  ㄴㄴ> Good example) MAX_ACCOUNTS_PER_PERSON

- e라는 문자도 변수이름으로 적합하지 못하다. 검색이 어렵기 때문이다.

- 긴 이름이 짧은 이름보다 좋다. 검색도 쉽다.

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

  ㄴ> Bad example) 

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

   ㄴㄴ> Good example) 

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;
}

 


 

인코딩을 피하라

- 유형이나 범위정보까지 인코딩에 넣으면 해독하기 어려워진다. 더불어 인코딩한 이름은 거의 발음하기 어려워 오타가 생기기 쉽다.

- 자바는 변수 이름에 타입을 인코딩할 필요가 없다. 이제는 헝가식 표기법이나 기타 인코딩 방식이 오히려 방해가 될 뿐이다.

  ㄴ> 헝가리안 표기법 : (예전엔 컴파일러가 타입을 점검하지 않아 타입을 기억해야 해서) 프로그래밍 언어에서 변수 및 함수의 인자 이름 앞에 데이터 타입을 명시하는 코딩 규칙

 

- 멤버 변수 접두어 m_ 등을 붙일 필요도 없다.

- 클래스와 함수는 접두어가 필요없을 정도로 작아야 마땅하다.

- 멤버 변술를 다른 색상으로 표시하거나 눈에 띄는 IDE를 사용해야 한다.

 

- (때로 인코딩이 필요한 경우) 인터페이스클래스와 구현클래스 중 인터페이스를 알리지 않으면서 인코딩하고 싶다.
이때, 구현클래스(ToyFactory)명을 인코딩하여 ToyFactoryImpl, KidultToyFactory 등으로 명명하는 것이 IToyFactory보다 낫다.

 


 

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

- 문자 하나만 사용하는 변수 이름은 문제가 있다. 

- 예외적으로 루프(범위가 작고 다른 변수와 충돌하지 않는 선)에서 반복횟수를 세는 i,j,k등은 괜찮다. (l는 절대 안됨!)

 


클래스나 객체 이름

- 명사나 명사구가 적합하다, 동사는 사용하지 않는다.

  ㄴ> Good example) Accout, Customer, AddressParser

- Manager, Processor, Data, Info 등과 같은 단어는 피하다.

 


메서드 이름

- 동사나 동사구가 적합하다.

- 접근자(Accessor), 변경자(Mutator), 조건자(Predicate)는 javabean 표준에 따라 값 앞에 get, set, is를 붙인다.

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

 

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

- 메서드는 인수를 설명하는 이름을 사용한다.

  ㄴ> Bad example) 

Complex fulcrumPoint = new Complex(23.0);

  ㄴ> Good example) 

Complex fulcrumPoint = Complex.FromRealNumber(23.0);

 


 

한 개념에 한 단어를 사용하라.  추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.

- 똑같은 메서드를 클래스마다 fetch, retrieve, get 등으로 제각각 부르는 경우

  ㄴ> Bad example) fetchStudent(), retrieveStudent(), getStudent()

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

  ㄴ> Bad example) NetworkController, NetworkManager, NetworkDriver


 

말장난을 하자마라.

Example)
위에서 "
한 개념에 한 단어를 사용하라"를 따르니 여러 클래스에 add라는 메서드가 생겼다.

- 모든 add메서드의 매개변수와 반환값이 모두 기존값 2개를 더하거나 이어서 새로운 값을 만든다는 맥락이 의미적으로 똑같다면 문제없다. 

만약 집합에 값을 추가하는 함수를 추가할 때 무엇으로 명명해야 하는가?

ㄴ> Good) 기존 add와 맥락이 다르므로 insert나 append라는 이름이 적당하다. 

ㄴ> Bad) 새 메서드를 add라 부른다면 이는 "말장난"이다.

 

 


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

- 모든 이름을 도메인 영역(=문제 영역)에서 가져오는 정책은 현명하지 못하다.
  예외적으로
적절한 '프로그래머 용어'가 없거나 도메인 영역과 관련 깊은 개념이라면 도메인 영역에서 이름을 가져온다.

- 기술개념에는 기술이름을 가져오는 정책은 현명한 선택이다.
  ㄴ> Good example) AcccountVisitor(VISITOR패턴), JobQueue

 


의미 있는 맥락을 추가하라.

- 대부분은 의미가 분명하지 않아 클래스, 함수, 이름 공간에 넣어 맥락을 부여한다.

- 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.

 

- Example1) 주소를 뜻하는 firstName, lastName, street, houseNumber, city, state, zipcode라는 변수가 존재

  ㄴ> Bad example) 어느 메서드가 state라는 변수 하나만 사용한다면 변수 state가 주소의 일부라는 사실을 알아채기 어렵다.

  ㄴ> Good example) addr라는 접두어를 추가해 addrFirstName, addrLastName, addrState라 쓰면 맥락이 좀 더 분명해진다. 변수가 좀 더 큰 구조에 속한다는 사실이 적어도 독자에게는 분명해진다.

  ㄴ> Better example) Address라는 클래스를 생성하면 더 좋다. 그러면 변수가 좀 더 큰 개념에 속한다는 사실이 컴파일러에게도 분명해진다.

 

- Example2)

  ㄴ> Bad example) 맥락이 불분명한 변수. 함수 이름은 맥락 일부만 제공하며, 알고리즘이 나머지 맥락을 제공한다. 함수를 끝까지 읽어보고 나서야 number, verb, pluralModifire라는 변수를 이해할 수 있다.

불행히도 독자가 맥락을 유추해야만 한다. 그냥 메서드만 훑어서는 세 변수의 의미가 불분명하다.

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";
		pluralModifire = "";
	} else {
		number = Integer.toString(count);
		verb = "are";
		pluralModifier = "s";
	}
	String guessMessage = String.format(
		"There %s %s%s", verb, number, candidate, pluralModifier
	};
	print(guessMessage);
}

  ㄴ> Good example)  함수를 작은 조각으로 쪼개고자 GuessStatisticsMessage라는 클래스를 만든 후 세 변수를 클래스에 넣었다.
그러면 세 변수는 맥락이 분명해진다.
즉 세 변수는 확실하게 GuessStatisticsMessage에 속한다.

이렇게 맥락을 개선하면 함수를 쪼개기가 쉬워지므로 알고리즘도 좀 더 명확해진다.

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, pluralModifre );
	}

	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";
	}
}

 

 


불필요한 맥락을 없애라

- '고급 휘발유 충전소 Gas Station Deluxe'라는 애플리케이션의 모든 클래스 이름을 GSD로 시작하겠다는 생각은 전혀 바람직하지 못하다.

- 일반적으로 짧은 이름은 의미가 분명한 경우에 한해 긴 이름보다 좋다.

- 이름에 불필요한 맥락을 추가하지 않도록 주의한다.

  ㄴ>Good example) Address는 클래스로 좋은 이름이다.

  ㄴㄴ> Bad example) accountAddress와 customerAddress는 Address 클래스 인스턴스로는 좋은 이름이나 클래스 이름으로는 적합하지 못하다.

 

  ㄴ> Good example) 포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, MAC, URI 라는 이름도 괜찮다. (의미가 좀 더 분명해질 수 있기 때문이다.)

 

 

 

 

 

 

 


출처

- 책제목 : "클린 코드" , 지은이 : 로버트 C. 마틴, 옮긴이 : 박재호, 이해영출판사 : 인사이트

반응형