[Effective Java] 아이템 51 : 메서드 시그니처를 신중히 설계하라(feat. 팀 컨벤션)
0. 들어가며
이번 아이템을 살짝 훑어보면, 좋은 메서드를 만들기 위한 규칙을 설명합니다.
먼저 규칙들을 설명하기에 앞서, 규칙들을 왜 제안하는지 생각해봤습니다.
- 일관성 없는 메서드명
- 메서드가 너무 많아서 뭘 써야할 지 모르는 상황
- 매개변수가 너무 많아서 헷갈리는 상황
대표적으로 이런 경우들로 생각해볼 수 있고, 아마 다들 한번쯤은 겪어보셨을 것이라 생각합니다.
이제 필자가 어떤 규칙들을 제안하고, 그 규칙들을 지키면 위와 같은 상황을 해결할 수 있는지 살펴보겠습니다.
1. 메서드 시그니처 규칙
필자가 제안한 메서드 시그니처 규칙은 다음과 같습니다.
- 메서드 이름을 신중히 짓자
- 표준 명명 규칙(아이템68)을 따르자
- 같은 패키지 속의 다른 이름들과 일관되게 짓자
- 개발자 커뮤니티에서 널리 받아들여지는 이름을 사용하자
- 너무 긴 이름은 피하자
- 편의 메서드를 너무 많이 만들지 말자
- 메서드가 너무 많은 인터페이스는 사용하기도, 구현하기도 힘듦
- ‘확신이 서지 않으면 만들지 말자’
- 매개변수 목록은 짧게 유지하자
- 4개가 넘어가는 순간 헷갈리기 쉬움
- ‘같은 타입이 연속해서 나오는 경우는 아주 안좋음’
- 이러한 문제를 해결하는 기술
- 여러 메서드로 쪼개기
- 매개변수 여러개를 묶는 클래스
- 빌더 패턴 사용
- 매개변수 타입으로는 인터페이스가 더 나음(아이템 68)
- boolean 보다는 원소 2개짜리 열거 타입이 더 나음
하지만 저는 이 보다 더 중요한 것이 있다고 생각하는데요.
그건 바로 내가 일하고있는 팀의 컨벤션 입니다.
2. 팀 컨벤션
저는 백엔드 개발자로 취업하여 2주 정도 일한 상태인데요.
적응 중이라 그런지 동료분들이 리뷰 해주실 때, 많이 알려주시는것 중 하나가 컨벤션 관련 내용입니다.
컨벤션을 유지하는 이유는 형식을 비슷하게 유지하면 코드를 읽을때 피로가 덜하고 가독성이 좋기 때문입니다.
그래서 제 경험을 바탕으로 어떤 부분들을 중점적으로 파악하면 좋을지 정리해봤습니다.
2.1 메서드명
메서드명은 보통 자주 쓰는 단어들을 많이 쓰게되니, 컨벤션을 딱 맞추지 않아도 됩니다.
하지만, 미묘하게 스타일이나 사용하는 단어가 다르기도 합니다.
길게 풀어쓰는 스타일 vs 짧고 제너럴한 단어로 표현하는 스타일
// 이렇게 길게 풀어쓰는사람이 있는가 하면
class Order {
public static Order createOrderWithMenu(Menu menu) { ... }
}
// 이렇게 이펙티브 자바에서 제안하는 팩터리 메서드 방식으로 작성하기도 함
class Order {
public static Order from(Menu menu) { ... }
}
영어 문장같은 메서드명 vs 가독성 좋은 짧고 간단한 단어
// 메서드의 동작을 영어로 풀어서 작성 하는가 하면
class ReviewService {
public boolean isPossibleToCreateReview(Review review) { ... }
}
// 심플한 단어로 표현하기도 함
class ReviewService {
public boolean canCreateReview(Review review) { ... }
}
메서드의 앞에 vs 메서드의 뒤에(boolean 반환 vs 예외 터트리기)
// isValid 라고 명명하고 boolean 을 반환하도록 구현하거나
class Something {
public boolean isValidSomething() { ... }
}
// 검증하여 메서드 자체에서 예외를 발생시키기도 함
class Something {
public void somethingValidation() { ... }
}
이 외에도 상황에 따라 컨벤션에 따라 다양한 단어들을 선택해서 사용합니다.
find~
vsget~
create~
vsget~
sort
vsorderBy
등등..
2.2 파라미터
파라미터는 보통 개수에 따라 어떻게 달라지는지 확인해볼 수 있습니다.
적당한 파라미터는 괜찮 vs DTO 조아
// 이렇게 4개 정도는 그냥 쓰는가 하면
class Something {
public void doSomething(int num, String str, char chr, boolean isReal) { ... }
}
// 2개 이상만 되도 DTO 로 만드는 사람도 있음
class Something {
public void doSomething(Dto dto) { ... }
}
record Dto(int num, String str) { }
왼쪽에서 오른쪽으로 vs 위에서 아래로
// 끝을 모르게 쭉쭉 붙이는 사람이 있는가 하면
class Something {
public void doSomething(int num, String str, char chr, boolean isReal, long howLong, short howShort) { ... }
}
// 이렇게 세로로 쭉 내려 쓰느 사람도 있음
class Something {
public void doSomething(
int num,
String str,
char chr,
boolean isReal,
long howLong,
short howShort
) {
// 구현부 ...
}
}
// 그리고 이렇게 첫번째 인자 이후로만 내려쓰는 경우도 있음
class Something {
public void doSomething(int num,
String str,
char chr,
boolean isReal,
long howLong,
short howShort) {
// 구현부 ...
}
}
위 예제의 3번째 경우는 회사에서 동료분이 쓰시는 스타일을 보고 배웠는데요.
2번째는 구현부와 들여쓰기가 같아 한눈에 들어오지 않을 수 있는데요.
3번째는 구현부와 들여쓰기 차이가 있기 때문에 파라미터 선언과 구현부가 확실히 구분된다는 장점이 있습니다.
이 외에도 다음과 같은 고려사항이 있을 수 있겠네요!
- Wrapper type 사용 여부
- Primitive type을 앞에 vs Reference type 을 앞에
등등