Clean Code_2장. 의미 있는 이름
#노마드코더 #북클럽 #노개북
TIL (Today I Learned)
변수, 함수, 인수, 클래스, 패키지, 소스 파일, 디렉터리 등등 소프트웨어의 다양한 부분에 이름이 붙는다. 이러한 이름을 잘 짓는 방법에 대해 공부했다.
오늘 읽은 범위
2장. 의미 있는 이름
책에서 기억하고 싶은 내용을 써보세요.
의도를 분명히 밝혀라
주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.
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 배열을 간단한 클래스로 만들어서 의미를 부여해도 좋다.
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell: gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
그릇된 정보를 피하라
- 나름 널리 쓰이는 의미 있는 단어라도 다른 의미로 해석될 수 있는 경우 피한다.
- 여러 계정을 그룹으로 묶을 때 실제 List가 아니라면
accountList
와 같이 명명하지 않는다.- 실제 컨테이너가 List 형인 경웅라도 컨테이너 유형을 이름에 넣지 않는 것이 바람직하다.
- 서로 흡사한 이름을 사용하지 않도록 주의한다.
- 유사한 개념은 유사한 표기법을 사용한다. (일관성 유지)
- 헷갈리는 이름을 쓰지 말아라.
- l(소문자 L)이나 O(대문자 o) 등을 이름으로 사용하면 1이나 0과 헷갈린다.
의미 있게 구분하라
- 컴파일러나 인터프리터만 통과하려고 연속된 숫자를 붙이거나 불용어를 추가하는 등의 방식은 적절치 않다.
- 예를 들어
zork
라는 변수가 있다고 해서theZork
라고 이름 지으면 안된다. Product
클래스가 있다고ProductInfo
,ProductData
등으로 이름 짓는 경우도 마찬가지.- 읽는 사람이 차이를 알도록 이름을 지어라.
발음하기 쉬운 이름을 사용하라
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";
}
발음하기 쉬운 이름
둘째 코드는 지적인 대화가 가능해진다.
검색하기 쉬운 이름을 사용하라.
- 문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
- 예를 들어 ‘7’을 검색하면 파일 이름이나 수식이 모두 검색된다.
- ‘e’와 같은 문자도 변수 이름으로 적합하지 못하다. 모든 프로그램, 거의 모든 문장에 등장할 것이다.
- 한 문자로 나타내는 것은 간단한 메서드에서 로컬 변수 정도?
- 이름 길이는 범위 크기에 비례해야 한다.
인코딩을 피하라
유형이나 범위 정보까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다.
- 헝가리식 표기법
- 변수의 타입을 이름에 포함하는 것
- 과거 윈도 C API에서 주로 사용하던 표기법
- 당시에는 컴파일러가 타입을 점검하지 않았기에 타입을 기억하기 위한 단서로 사용되었다.
- 현재는 컴파일러가 타입을 기억하고 강제하기 때문에 자주 사용되지 않는다.
- 멤버 변수 접두어
- 멤버 변수에
m_
이라는 접두어를 붙이는 것 -> 이제는 그럴 필요가 없다.
- 멤버 변수에
- 인터페이스 클래스와 구현 클래스
- 도형을 생성하는
ABSTRACT FACTORY
를 구현한다고 할 때, 이 팩토리는 인터페이스 클래스다. - 구현은 구체 클래스(concrete class)에서 한다.
- 옛날 코드에서는 인터페이스임을 알리기 위해 접두어 I를 붙였다.
- 저자의 의견으로는 다음과 같은 이름이 IShapeFactory보다 좋다고 한다.
- ShapeFactoryImp
- CShapeFactory
- 도형을 생성하는
자신의 기억력을 자랑하지 마라
- 명료함이 최고다
클래스 이름
- 클래스 이름과 객체 이름은 명사나 명사구가 적합하다.
Customer
,WikiPage
,Account
,AddressParser
등 ⭕Manager
,Processor
,Data
,Info
등 ⚠️- 동사 ❌
메서드 이름
- 메서드 이름은 동사나 동사구가 적합하다
postPayment
,deletePage
,save
등
-
접근자, 변경자, 조건자는 javabean 표준에 따라 값 앞에
get
,set
,is
를 붙인다.string name = employee.getName(); customer.setName("mike"); if (paycheck.isPosted())...
- 생성자를 중복 정의(overload) 할 때는 정적 팩토리 메서드를 사용한다.
- 메서드는 인수를 설명하는 이름을 사용한다.
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
위 코드보다 아래 코드가 좋다.
Complex fulcrumPoint = new Complex(23.0);
기발한 이름은 피하라
특정 문화에서만 사용하는 농담은 피하고, 의도를 분명하고 솔직하게 표현하라.
한 개념에 한 단어를 사용하라
- 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다
- 똑같은 메서드를 클래스마다
fetch
,retrieve
,get
으로 제각각 부르면 혼란스럽다
- 똑같은 메서드를 클래스마다
말장난을 하지 마라
- 한 단어를 두 가지 목적으로 사용하지 마라.
- 지금까지 구현한
add
메서드가 기존 값 두 개를 더하거나 이어서 새로운 값을 만든다고 하자 - 새로 작성하는 메서드는 집합에 값 하나를 추가한다고 할 때 이 메서드를
add
라 지어도 괜찮을까?- 이 메서드는 기존의
add
메서드와 맥락이 다르다. 그러므로insert
나append
라는 이름이 적당하다.
- 이 메서드는 기존의
- 지금까지 구현한
- 다른 개념에 같은 단어를 사용한다면 말장난에 불과하다.
해법 영역에서 가져온 이름을 사용하라
- 코드를 읽는 사람도 프로그래머다
- 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 좋다.
- 모든 이름을 문제 영역(domain)에서 가져오는 정책은 현명하지 못하다.
문제 영역에서 가져온 이름을 사용하라
- 적절한 프로그래머 용어가 없다면 문제 영역에서 이름을 가져온다.
- 그러면 전문가에게 의미를 물어 파악할 수 있다.
의미 있는 맥락을 추가하라
- 스스로 의미가 분명한 이름이 있지만 대다수 이름은 그렇지 못하다.
- 그래서 클래스, 함수, 이름 공간에 넣어 맥락을 부여한다.
-
모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.
- 예를 들어
firstName
,lastName
,street
,houseNumber
,city
,state
,zipcode
라는 변수가 있을 때 변수를 훑어보면 주소라는 사실을 알 수 있다. - 하지만
state
변수 하나만 사용된다면state
가 주소 일부라는 사실을 알기 힘들다 - 이때
addr
라는 접두어를 추가해addrFirstName
,addrLastName
,addrState
라 쓰면 맥락이 분명해진다. Address
라는 클래스를 생성하면 더 좋다.
불필요한 맥락을 없애라
- IDE는 개발자를 지원하는 도구다. IDE를 방해할 이유가 없다.
- 일반적으로 짧은 이름이 긴 이름보다 좋지만 이는 의미가 분명한 경우에 한해서다.
마무리
- 좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적 배경이 같아야 한다.
- 암기는 요즘 나오는 도구에게 맡기고, 우리는 표나 자료 구조처럼 읽히는 코드를 짜는 데만 집중해야 한다.
- 코드를 개선하려는 노력을 중단해서는 안 된다.
오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요
저자의 말대로 소프트웨어에서 변수, 함수, 인수, 클래스, 패키지, 소스 파일, 디렉터리 등등 다양한 부분에 이름을 붙여야 한다. 프로그래밍 언어를 처음 배울 때 우스갯 소리로 들었던 것이 코딩보다 이름 짓는 것이 더 어렵다는 말이었다.
그만큼 적절한 이름을 짓는 것은 쉬운 일이 아니다.
내가 평소에 코드를 짜며 이름을 지을 때는 보통 내가 학습한 자료나 강의 등에서 이름 붙이는 방식을 따라 갔던 것 같다. 클래스와 객체 이름은 명사로 짓고, 메서드 이름은 동사로 짓는 등의 널리 알려진 방식에서는 잘 따르고 있었지만 인터페이스에 ‘I’ 접두사를 붙인다거나 nameTextView
와 같이 타입을 붙이는 등의 작명 법은 나도 그렇고 아직 다른 사람들도 많이 사용하는 방법이라서 무조건 적으로 이 책의 내용을 따를 것이 아니라 함께 개발하는 사람들과 맞춰 바꿔 나가는 방향으로 하면 좋을 것 같다.
궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
정적 팩토리 메서드: 객체 생성 역할을 하는 클래스 메서드
// LocalTime.class
...
public static LocalTime of(int hour, int minute) {
ChronoField.HOUR_OF_DAY.checkValidValue((long)hour);
if (minute == 0) {
return HOURS[hour];
} else {
ChronoField.MINUTE_OF_HOUR.checkValidValue((long)minute);
return new LocalTime(hour, minute, 0, 0);
}
}
...
// hour, minutes을 인자로 받아서 9시 30분을 의미하는 LocalTime 객체를 반환한다.
LocalTime openTime = LocalTime.of(9, 30);
위 코드에서 LocalTime 클래스의 of
메서드처럼 직접적으로 생성자를 통해 객체를 생성하는 것이 아닌 메서드를 통해서 객체를 생성하는 것을 정적 팩토리 메서드라고 한다.
public enum Color {
RED,
BLUE;
}
...
Color redColor = Color.valueOf("RED");
Color blueColor = Color.valueOf("BLUE");
위 코드처럼 enum 요소를 조회할 때 사용하는 valueOf
도 정적 팩토리 메서드의 일종이라고 할 수 있다.