리팩토링 - 냄새 1. 이해하기 힘든 이름

김상운(개발둥이)·2022년 3월 27일
0

리팩토링

목록 보기
2/17
post-thumbnail

들어가기

해당 포스팅은 인프런 백기선님의 '리팩토링'을 학습 후 정리한 내용입니다.



냄새 1. 이해하기 힘든 이름

Mysterius Name

  • 깔끔한 코드에서 가장 중요한 것 중 하나가 바로 “좋은 이름”이다.
  • 함수, 변수, 클래스, 모듈의 이름 등 모두 어떤 역할을 하는지 어떻게 쓰이는지 직관적이어야 한다.
  • 사용할 수 있는 리팩토링 기술
  • 함수 선언 변경하기 (Change Function Declaration)
  • 변수 이름 바꾸기 (Rename Variable)
  • 필드 이름 바꾸기 (Rename Field)


리팩토링 1. 함수 선언 변경하기

Change Function Declaration

함수 이름 변경하기, 메소드 이름 변경하기, 매개변수 추가하기, 매개변수 제거하기, 시그니처 변경하기

  • 좋은 이름을 가진 함수는 함수가 어떻게 구현되었는지 코드를 보지 않아도 이름만 보고도이해할 수 있다.
  • 좋은 이름을 찾아내는 방법? 함수에 주석을 작성한 다음, 주석을 함수 이름으로 만들어 본다.
  • 함수의 매개변수는 함수 내부의 문맥을 결정한다. (예, 전화변호 포매팅 함수)
    • 전화번호를 필드로하는 회원 객체를 매개변수로 할지
  • 의존성을 결정한다. (예, Payment 만기일 계산 함수)
    • 단순 숫자만 넘길지, 객체를 넘길지

예제 코드

package me.whiteship.refactoring._01_smell_mysterious_name._01_before;

import org.kohsuke.github.*;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class StudyDashboard {
    @Value("token")
    private static String token;

    private Set<String> usernames = new HashSet<>();

    private Set<String> reviews = new HashSet<>();

    private void studyReviews(GHIssue issue) throws IOException {
        List<GHIssueComment> comments = issue.getComments();
        for (GHIssueComment comment : comments) {
            usernames.add(comment.getUserName());
            reviews.add(comment.getBody());
        }
    }

    public Set<String> getUsernames() {
        return usernames;
    }

    public Set<String> getReviews() {
        return reviews;
    }

    public static void main(String[] args) throws IOException {

        GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
        GHRepository repository = gitHub.getRepository("whiteship/live-study");
        GHIssue issue = repository.getIssue(30);

        StudyDashboard studyDashboard = new StudyDashboard();
        studyDashboard.studyReviews(issue);
        studyDashboard.getUsernames().forEach(System.out::println);
        studyDashboard.getReviews().forEach(System.out::println);
    }
}

설명

깃헙 whiteship/live-study 레포지토리에서 30 번째 이슈의 작성자와 리뷰를 출력한다.

냄새

studyDashboard.studyReviews(issue); 
//studyReviews 메서드의 이름이 알맞지 않아 
//loadReviews 이름으로 수정.

해결

인텔리제이를 사용하여 윈도우 기준 shift + F6 눌러 메서드 이름을 loadReviews 로 수정한다.

리팩토링 후 코드

package me.whiteship.refactoring._01_smell_mysterious_name._01_change_method_declaration;

import org.kohsuke.github.*;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class StudyDashboard {

    @Value("token")
    private static String token;

    private Set<String> usernames = new HashSet<>();

    private Set<String> reviews = new HashSet<>();

    private void studyReviews(GHIssue issue) throws IOException {
        List<GHIssueComment> comments = issue.getComments();
        for (GHIssueComment comment : comments) {
            usernames.add(comment.getUserName());
            reviews.add(comment.getBody());
        }
    }

    public Set<String> getUsernames() {
        return usernames;
    }

    public Set<String> getReviews() {
        return reviews;
    }

    public static void main(String[] args) throws IOException {
        GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
        GHRepository repository = gitHub.getRepository("whiteship/live-study");
        GHIssue issue = repository.getIssue(30);

        StudyDashboard studyDashboard = new StudyDashboard();
        studyDashboard.studyReviews(issue);
        studyDashboard.getUsernames().forEach(System.out::println);
        studyDashboard.getReviews().forEach(System.out::println);
    }
}


리팩토링 2. 변수 이름 바꾸기

Rename Variable

  • 더 많이 사용되는 변수일수록 그 이름이 더 중요하다.
  • 람다식에서 사용하는 변수 vs 함수의 매개변수
  • 다이나믹 타입을 지원하는 언어에서는 타입을 이름에 넣기도 한다.
  • 여러 함수에 걸쳐 쓰이는 필드 이름에는 더 많이 고민하고 이름을 짓는다

예제 코드

public class StudyDashboard {
    @Value("token")
    private static String token;

    private Set<String> usernames = new HashSet<>();

    private Set<String> reviews = new HashSet<>();

    private void loadReviews(GHIssue issue) throws IOException {
        List<GHIssueComment> comments = issue.getComments();
        for (GHIssueComment comment : comments) {
            usernames.add(comment.getUserName());
            reviews.add(comment.getBody());
        }
    }

    public Set<String> getUsernames() {
        return usernames;
    }

    public Set<String> getReviews() {
        return reviews;
    }

    public static void main(String[] args) throws IOException {

        GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
        GHRepository repository = gitHub.getRepository("whiteship/live-study");
        GHIssue issue = repository.getIssue(30);

        StudyDashboard studyDashboard = new StudyDashboard();
        studyDashboard.loadReviews(issue);
        studyDashboard.getUsernames().forEach(System.out::println);
        studyDashboard.getReviews().forEach(System.out::println);
    }
}

냄새

gitHub, repository, issue 변수를 초기화 하는 코드를 loadReviews 메서드에서 초기화 한다면 main 함수가 더 깔끔해질거 같다!

해결

함수의 매개변수 부분을 수정하기 위해서는 인텔리제이에서 윈도우 기준
ctrl + F6 를 눌러 수정한다.

GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
GHIssue issue = repository.getIssue(30);

위 코드를 loadReviews 내부에서 초기화 하여 사용한다.

또한 해당 함수에서 매개변수를 수정하기 위해 인텔리제이에서 윈도우 기준 ctrl + F6 를 눌러 수정할 수 있다.

리팩토링 후 코드

package me.whiteship.refactoring._01_smell_mysterious_name._02_rename_variable;

import org.kohsuke.github.*;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class StudyDashboard {

    @Value("token")
    private static String token;
    
    private Set<String> usernames = new HashSet<>();

    private Set<String> reviews = new HashSet<>();

    /**
     * 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어옵니다.
     * @throws IOException
     */
    private void loadReviews() throws IOException {
        GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
        GHRepository repository = gitHub.getRepository("whiteship/live-study");
        GHIssue issue = repository.getIssue(30);

        List<GHIssueComment> comments = issue.getComments();
        for (GHIssueComment comment : comments) {
            usernames.add(comment.getUserName());
            this.reviews.add(comment.getBody());
        }
    }

    public Set<String> getUsernames() {
        return usernames;
    }

    public Set<String> getReviews() {
        return reviews;
    }

    public static void main(String[] args) throws IOException {
        StudyDashboard studyDashboard = new StudyDashboard();
        studyDashboard.loadReviews();
        studyDashboard.getUsernames().forEach(name -> System.out.println(name));
        studyDashboard.getReviews().forEach(review -> System.out.println(review));
    }
}


리팩토링 3. 필드 이름 바꾸기

Rename Field

  • Record 자료 구조의 필드 이름은 프로그램 전반에 걸쳐 참조될 수 있기 때문에 매우 중요
    하다.
  • Record 자료 구조: 특정 데이터와 관련있는 필드를 묶어놓은 자료 구조
  • 파이썬의 Dictionay, 또는 줄여서 dicts.
  • C#의 Record.
  • 자바 14 버전부터 지원. (record 키워드)
  • 자바에서는 Getter와 Setter 메소드 이름도 필드의 이름과 비슷하게 간주할 수 있다.

예제코드

package me.whiteship.refactoring._01_smell_mysterious_name._02_rename_variable;

import org.kohsuke.github.*;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class StudyDashboard {

    @Value("token")
    private static String token;

    private Set<String> usernames = new HashSet<>();

    private Set<String> reviews = new HashSet<>();

    /**
     * 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어옵니다.
     * @throws IOException
     */
    private void loadReviews() throws IOException {
        GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
        GHRepository repository = gitHub.getRepository("whiteship/live-study");
        GHIssue issue = repository.getIssue(30);

        List<GHIssueComment> comments = issue.getComments();
        for (GHIssueComment comment : comments) {
            usernames.add(comment.getUserName());
            this.reviews.add(comment.getBody());
        }
    }

    public Set<String> getUsernames() {
        return usernames;
    }

    public Set<String> getReviews() {
        return reviews;
    }

    public static void main(String[] args) throws IOException {
        StudyDashboard studyDashboard = new StudyDashboard();
        studyDashboard.loadReviews();
        studyDashboard.getUsernames().forEach(name -> System.out.println(name));
        studyDashboard.getReviews().forEach(review -> System.out.println(review));
    }
}

냄새

username, review 필드를 서로 다른 자료구조에서 따로 관리하기 보단 객체화하면 더 깔끔해질 수 있을거 같다.

해결

JDK 14 이상에서 사용할 수 있는 record 를 사용

리팩토링 후 코드

package me.whiteship.refactoring._01_smell_mysterious_name.practice;

import me.whiteship.refactoring._01_smell_mysterious_name._03_rename_field.StudyReview;
import org.kohsuke.github.*;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class StudyDashboard {
    @Value("token")
    private static String token;

    private Set<StudyReview> studyReviews = new HashSet<>();

    private void loadReviews() throws IOException {
        GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build();
        GHRepository repository = gitHub.getRepository("whiteship/live-study");
        GHIssue issue = repository.getIssue(30);

        List<GHIssueComment> reviews = issue.getComments();
        for (GHIssueComment review : reviews) {
            studyReviews.add(new StudyReview(review.getUserName(), review.getBody()));
        }
    }

    public Set<StudyReview> getStudyReviews() {
        return studyReviews;
    }

    public static void main(String[] args) throws IOException {

        StudyDashboard studyDashboard = new StudyDashboard();
        studyDashboard.loadReviews();
        studyDashboard.getStudyReviews().forEach(System.out::println);
    }
}

StudyReview

public record StudyReview(String reviewer, String review) {
}

record 는 생성자를 작성하지 않아도 되고 toString, equals, hashCode 메소드에 대한 구현을 자동으로 제공

profile
공부한 것을 잊지 않기 위해, 고민했던 흔적을 남겨 성장하기 위해 글을 씁니다.

0개의 댓글