변수, 메소드, 클래스 등의 이름에 일관성이 없고, 쓰임을 분명히 나타내지 않는다면 어떻게 될까?
이름을 주의깊게 살펴, 더 좋은 이름이 떠오른다면 기꺼이 수정할 수 있어야한다.
프레임워크마다 코드 컨벤션이 달라지기도 한다!
하지만 자바는 보통 오라클의 자바 코드 컨벤션을 따른다.
이것에 대해 살펴보자!
예를 살펴보자.
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList) {
if (x[0] == 4) {
list1.add(x);
}
}
return list1;
}
코드의 공백과 들여쓰기도 적당하고 변수, 상수도 적지만 코드가 하는 일을 짐작하기 어렵다.
이름들의 의도가 불분명해서 코드의 맥락이 없다.
위 코드는 암암리에 독자가 다음과 같은 정보를 안다고 가정한다
지뢰 찾기 게임을 만든다고 가정하자, theList는 게임판이다. 게임판에서 각 칸은 단순 배열로 표현한다.
배열에서 0번째 값은 칸의 상태를 뜻한다. 값 4는 깃발이 꽂힌 상태를 가리킨다.
상황을 알았으니 이름을 다시 써보자.
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라는 상수를 감춰도 좋을 것 같다.
public List<int[]> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
처음보다 코드를 이해하기가 굉장히 쉬워졌다.
모든 변수의 이름을 10~16의 길이로 작성하기 위해 애쓸 필요는 없겠지만, 길다면 그 이름이 적당한지 확인할 필요가 있다.
[너무 긴 이름]
numberOfPeopleOnTheUsOlympicTeam
numberOfSeatsInTheStadium
maximunNumberOfPointsInMordernOlympics
[너무 짧은 이름]
n, np, ntm
n, ns, nsisd
m, mp, max, points
[적당한 이름]
numTeamMembers, teamMemberCount
numSeatsInStadium, seatCount
teamPointsMax, pointsRecord
private void validateNumericPosition(String[] expressionAsArray) {
for (int i = 0; i < expressionAsArray.length; i += 2) {
...
}
}
위 코드에서 2가 가르키는 내용은 뭘까?
협업자가 이를 알기위해서는 코드를 분석하는 불필요한 시간을 가져야한다.
하지만 아래와 같이 작성한다면?
private void validateNumericPosition(String[] expressionAsArray) {
int numberIndex = 2;
for (int i = 0; i < expressionAsArray.length; i += numberIndex) {
...
}
}
2를 numberIndex라고 변수처리하며 의도를 정확히 밝힐 수 있다.
이름이 지나치게 짧을 경우 변수의 용도를 알기 어렵다.
public class User{
String userName;
int userAge;
...
}
위 코드를 보면, 클래스의 이름에서 user정보를 담고 있다는 것을 알 수 있다.
변수명에 정확한 의미를 담으려는 의도는 좋지만, 클래스명에서 user정보임을 알 수 있기때문에 굳이 멤버변수의 이름을 userName, userAge로 지을 필요가 없다.
public class User{
String name;
int age;
...
}
멤버변수명은 이처럼 수정해도 좋다.
자바에서는 접두어 is 를 붙여 불린 메소드나 변수명을 짓는다.
그러나, 접두어가 없는 이름이 읽고 이해하는데 더 쉬운 경우가 있다.
private static final String FORMAT_SYMBOL = "//(.*)\\\\n(.*)";
public static List<String> customSplit(String input) {
Matcher matcher = Pattern.compile(FORMAT_SYMBOL).matcher(input);
if (matcher.find()) {
return Arrays.asList(matcher.group(2).split(Pattern.quote(matcher.group(1))));
}
...
}
위 코드의 리턴 부분을 보았을 때, 한 줄에 많은 기능이 있어서 코드 읽기가 어렵다.
private static final String FORMAT_SYMBOL = "//(.*)\\\\n(.*)";
public static List<String> customSplit(String input) {
Matcher matcher = Pattern.compile(FORMAT_SYMBOL).matcher(input);
if (matcher.find()) {
String splitSymbol = matcher.group(1);
String content = matcher.group(2);
return Arrays.asList(content.split(Pattern.quote(splitSymbol)));
}
...
}
splitSymbol, content 변수로 선언해 줌으로써 이전보다 훨씬 읽기가 수월해진다!
private List<Double> numberList = new ArrayList<>();
private List<String> operatorList = new ArrayList<>();
List 대신 다른 자료형(Set…)을 써야 하는 경우가 오면 어떻게 해야 할까?
기존 변수명이 적절한 의미를 나타내지 못하게 되므로 결국 변수명을 변경해야 하는 번거로움이 생긴다.
변수 이름에 자료형을 쓰지 않아도 타입을 통해 충분히 어떤 변수인지 파악이 가능하다.
private List<Double> numbers = new ArrayList<>();
private List<String> operators = new ArrayList<>();
[좋은 예]
revenueTotal
expenseTotal
revenueAverage
expenseAverage
[나쁜 예]
totalRevenue
expenseTotal
revenueAverage
averageExpense
루틴이 하는 모든것을 표현하라
의미가 없거나 모호하거나 뚜렷한 특징이 없는 동사들을 피하라.
HandleCalculation(), PerformServices()과 같은 이름들은 그 루틴이 무엇을 하는지 알 수 없다.
실제로 루틴은 잘 설계되었지만, 루틴의 이름에 뚜렷한 특징이 없기 때문에 문제가 되기도 한다. 만약 HandleOutput()이 FormatAndPrintOutput()으로 대체된다면, 루틴이 무엇을 하는지 상당히 정확하게 이해할 수 있다.
! 루틴이 처리하는 연산 자체가 모호하기 때문에 이름이 모호해지는 경우도 있다.
루틴의 목적이 취약하다는 것이 문제이고, 서투른 이름은 그로 인한 증상이다.
⚡️이런 경우 해당 루틴을 적절하게 리팩토링하여 명확한 처리를 하도록 해야 한다.
루틴 이름을 숫자만으로 구분하지 말라.
OutputUser1(), OutputUser2()은 잘못된 이름이다.
함수의 이름을 지을 때, 리턴 값에 대한 설명을 사용하라.
pen.CurrentColor()는 함수가 리턴하는 것을 정확하게 보여주고 있기 때문에 좋은 이름이다.
프로시저의 이름을 지을 때, 확실한 의미를 갖는 동사 다음에 객체를 사용하라.
프로시저의 이름은 프로시저가 무엇을 하는지를 반영해야 하기 때문에, 객체에 대한 연산은 동사+객체 형태의 이름을 갖는다
CheckOrderInfo()는 좋은 이름이다.
공통적인 연산을 위한 규약을 만들어라.
employee.id.Get()
dependent.GetId()
supervisor()
candidate.id()
위 코드는 모두 특정 객체의 식별자를 얻기 위한 코드이다. Employee 클래스는 자신의 id 객체를 노출하고, id 객체는 Get() 루틴을 노출했으며, Dependent 클래스는 GetId() 루틴을 노출했다.
Supervisor 클래스는 id를 기본 리턴캆으로 만들었고, Candidate 클래스는 id 객체의 기본 리턴 값이 id라는 사실을 이용하여 id 객체를 노출시켰다.
id를 가져오는 이름 규칙이 있었다면 이러한 난잡한 코드가 생성되는 현상은 막을 수 있을 것이다.
루프가 길어질때에도 의미있는 이름이 좋다.
코드는 자주 변경되고 확장되고 다른 프로그램에 복사되기 때문에, 많은 숙련된 프로그래머들은 i와 같은 이름들은 피한다.
여러 개의 중첩된 루프가 있다면, 가독성을 향상시키기 위해서 좀 더 긴 이름으로 루프 변수들을 작성한다.
// 중첩된 루프에서 좋은 루프 이름을 갖는 예제
for ( teamIndex = 0; teamIndex < teamCount; teamIndex++ ) {
for ( eventIndex = 0; eventIndex < eventCount[ teamIndex ]; eventIndex++ ) {
score[ teamIndex ][ eventIndex ] = 0;
}
}
임시 변수는 계산의 중간 결과를 보관하기 위한 임시 저장소로 사용되고 보조 수단으로 사용되는 값을 보관하기 위해 사용된다.
일반적으로 temp, x 또는 그 밖의 모호하고 설명적이지 않은 이름으로 만들어진다.
일반적으로, 임시 변수는 프로그래머가 프로그램을 완벽하게 이해하지 못하고 있다는 신호이다.
게다가 변수가 공식적으로 ‘임시’상태이기 때문에, 프로그래머는 임시 변수를 다른 변수들보다 별 생각 없이 다루게 되어 오류가 발생할 가능성이 높아진다.
프로그래밍은 사회적 활동이다.
발음하기 어려운 이름은 본인의 사고에도 방해가 되고 사람들과 협업할 때는 더욱 그렇다.
add라는 의미가 기존 두 값을 더하거나 이러서 새로운 값을 만드는데 쓰였다고 하자.
새로 작성하는 메서드는 집합에 값 하나를 추가하는데, 일관성을 고려한답시고 이 메서드를 add라고 부르면 안된다.
// Bad
class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
private final String pszqint = "102";
/* ... */
};
코드는 작성의 편의성보다, 가독성에 더 중점을 두고 작성해야한다!
참고사이트
https://tecoble.techcourse.co.kr/post/2020-04-24-variable_naming/
https://remotty.github.io/blog/2014/03/01/hyogwajeogin-ireumjisgi/
https://his2070.tistory.com/6