최근 사내 프로젝트를 진행하면서 리팩터링 할 때마다 변수의 이름을 변경했다.
여러 자료를 보고 이게 최선의 이름이라고 작명한 이후 다음 날 다시 보면 또 뭔가 아닌 것 같아서 변경하고..
변수명, 클래스명, 인터페이스명을 짓는 것은 상당히 고된 일 중 하나이다.
이번에는 한 챕터를 공부하고 예제 + 실무에 적용하는 과정을 반복하여 습관을 만들려고 한다.
하루에 한 챕터에 대해 예제와 이론을 책과 강의로 읽어보고 그 지식 그대로 다음날 실무에 가서 적용할 수 있는 부분을 찾아 적용하려고 한다.
함수 선언 변경
책에서도 말하지만 중점은 함수 이름을 잘 지어두면 본문 코드를 볼 이유가 사라진다. 코드를 내포할 수 있는 이름을 지으려면 어떻게 해야 할까?
단순히 생각하면 전제조건이 있다. 함수가 단 하나의 일을 해야한다. 함수가 여러 개의 일을 한다면 분명 함수명을 짓기가 어려울 것이다. 그렇다면 그 함수는 분리가 필요하다는 느낌을 받아야 한다.
또한 함수의 매개변수도 중요한다.
함수의 파라미터는 의존성을 결정하고, 내부의 문맥을 결정한다.
다음과 같은 함수를 보자.
private void studyReviews(GHIssue issue) throws IOException {
List<GHIssueComment> comments = issue.getComments();
for (GHIssueComment comment : comments) {
usernames.add(comment.getUserName());
reviews.add(comment.getBody());
}
}
함수는 다음과 같다.
깃허브의 이슈의 달린 리뷰를 담아서 해당 리뷰를 단 사람의 이름과 내용을 클래스 필드 변수에 담는 역할이다.
이를 사용하게 되는 클라이언트 코드는 다음과 같다.
GHIssue issue = repository.getIssue(30);
StudyDashboard studyDashboard = new StudyDashboard();
studyDashboard.studyReviews(issue);
studyDashboard.getUsernames().forEach(System.out::println);
studyDashboard.getReviews().forEach(System.out::println);
studyReviews(issue) 라는 함수 시그니처는 적절한지 고민해보자.
좋은 함수명은 내부 코드를 보지 않고 이해할 수 있어야 한다.
여기서 발생하는 의문점이 무엇일까?
동사가 없다. 무엇을 하려는 건지 알기 힘들다. 이슈를 매개변수로 받았지만 해당 이슈로 무엇을 한다는 겄지 의미가 내포되어 있지 않다고 생각한다.
책에서는 이러한 함수명을 생각하기 전 함수에 주석을 달아 무엇을 하려는 건지 적어보라고 조언한다. 그대로 한다면 위의 작성한 코멘트와 같이 작성할 것 같다.
"깃허브의 이슈의 달린 리뷰를 담아서 해당 코멘트를 단 이름과 내용을 클래스 필드 변수에 담는 역할이다. "
내 생각은 get을 붙이는 것은 어떻까? getStudyReviews 하지만 이는 자바빈즈 스펙과 약간 겹치는 느낌이 없잖아 있다.
마치 필드의 값을 가져오는 것 같아 혼란을 야기할 수 있다고 생각한다.
load는 어떨까?
loadReviews(GHIssue issue)는 매개변수까지 포함 된 맥락에서 이슈에 등록된 리뷰를 가져온다는 내용이 충분히 담겨있는 것 같다.
이제 매개변수에 대해 생각해보자.
다음과 같은 상황이 존재할 수 있다.
1. 리뷰할 수 있는 issue가 여러 개여서 변경될 가능성이 있다면 현재 함수는 적절해 보인다.
2. 만약 issue가 고정적이라면 이를 파라미터로 받는 것이 적절하지 않고 함수 내부에 포함되어 있어도 될 것 같다.
2번째 상황에 맞게 코드를 고쳐보자.
private void loadReviews() throws IOException {
GitHub gitHub = new GitHubBuilder().withOAuthToken("oauth").build();
GHRepository repository = gitHub.getRepository("repository");
GHIssue issue = repository.getIssue(30);
List<GHIssueComment> comments = issue.getComments();
for (GHIssueComment comment : comments) {
usernames.add(comment.getUserName());
reviews.add(comment.getBody());
}
}
함수 명을 지을 때 함수의 동작 방식이 아닌 의도가 드러나게 짓어야 된다.
매개 변수가 없는 함수 명인 loadReviews의 함수의 의도를 파악할 수 있는가?
나는 전체적인 맥락에서 해당 함수명은 괜찮은 것 같다.
클래스 이름이 StudyDashborad라는 점과 그 내부에서 loadReviews는 충분히 스터디에 관련된 리뷰를 로딩하는 함수라는 것을 알 수 있기 때문이다.
변수 이름 변경하기
변수 이름의 중요도는 scope에 따라 정해질 수 있다. scope이 클수록 사용되는 빈도수가 많아지기 때문이다.
1. 람다식은 범위가 작다.
studyDashboard.getUsernames().forEach(name -> System.out.println(name));
studyDashboard.getReviews().forEach(review -> System.out.println(review));
람다식 내부에 사용되는 변수는 대부분의 경우에는 어떤 타입에 어떤 데이터가 들어있는지 알고 있는 경우가 많다.
나도 한 글자 정도로 줄여서 사용하기도 하고 메서드 레퍼런스로 변경해버리기도 한다.
studyDashboard.getUsernames().forEach(System.out::println);
studyDashboard.getReviews().forEach(System.out::println);
2. 함수 내부의 변수명
private void loadReviews() throws IOException {
List<GHIssueComment> comments = issue.getComments();
for (GHIssueComment comment : comments) {
usernames.add(comment.getUserName());
this.reviews.add(comment.getBody());
}
}
review를 가져오는데 함수 내부에 review를 표현하는 변수가 없다. 물론 깃 헙 상에서는 comment가 틀린 것은 아니지만
reviews라고 표현하는 것이 어떻까?
private void loadReviews() throws IOException {
...
List<GHIssueComment> reviews = issue.getComments();
for (GHIssueComment review : reviews) {
usernames.add(review.getUserName());
this.reviews.add(review.getBody());
}
}
필드 이름 변경
Record의 필드는 프로그램 전반에 걸쳐 참조될 수 있기에 매우 중요할 수 있다.
private Set<String> usernames = new HashSet<>();
private Set<String> reviews = new HashSet<>();
클래스 내부에선 usernames 보다 reviewers라는 것이 더 좋아 보인다. 이를 Record를 사용하도록 변경해보자.
public record StudyReview(String reviewers, String review) {}
이제 다음과 같이 코드를 개선할 수 있다.
for (GHIssueComment review : reviews) {
studyReviews.add(new StudyReview(review.getUserName(), review.getBody()));
}
최종적인 클라이언트 코드는 다음과 같다.
public static void main(String[] args) throws IOException {
StudyDashboard studyDashboard = new StudyDashboard();
studyDashboard.loadReviews();
studyDashboard.getStudyReviews().forEach(System.out::println);
}
레코드가 불변하고 이미 자바의 관습을 이미 압축하여 포함하고 있다는 특성 때문에 다음과 같이 줄일 수 있다는 것도 장점이다.
역시 프로그래밍 언어의 최신 특성을 이용한 가독성을 높이는 방법도 좋은 방법으로 보인다.
Mystreious Name
프로그래밍에서 이름을 짓는 것은 가장 어렵기로 손꼽히는 것에 포함된다.
이를 우리가 처음부터 잘 짓는 것도 좋지만 거의 불가능하다고 생각하자.
처음 코드를 작성했을 때 best naming이었을지 몰라도 다시 해당 코드를 보게 되는 일이 생겼을 때 의도가 표현되지 않을 수 있다.
그럴 때마다 우리는 적절한 변수명으로 리팩터링을 진행하여 점진적인 개선하는 습관이 더 중요하지 않을까 생각한다.
만약 우리가 적절한 이름이 떠오르지 않는다면 다음을 생각해 볼 수 있다.
설계에 더 근본적인 문제가 숨어 있지 않을까?
적합하지 않은 행위를 할 수도 있고, 여러 가지 작업을 해서 복합적인 의도를 표현하려고 변수명이 길어지거나 이러한 상황에는 변수명을 짓기보다는 좀 더 큰 그림을 의심해보자.
'독서에서 한걸음' 카테고리의 다른 글
| 긴 함수 (1) (0) | 2022.12.02 |
|---|---|
| Refactoring_2 중복 코드 (0) | 2022.11.27 |
| Clean Code .Part14 (1) (0) | 2022.08.15 |
| Refactoring .Chapter 02 (0) | 2022.08.15 |
| Refactoring .Chapter 01 (0) | 2022.08.06 |
댓글