728x90
반응형
이해하기 힘든 이름 (Mysterius Name)
- 깔끔한 코드에서 가장 중요한 것 중 하나가 바로 "좋은 이름"
- 좋은 이름은 코드를 이해하는 데에 많은 도움을 준다
- 함수, 변수, 클래스, 모듈의 이름 등 모두 어떤 역할을 하는지 어떻게 쓰이는지 직관적이어야 함
- 처음에 적절하다고 생각했던 이름도 시간이 지나고 보면 그렇지 않게 보일 수 있으며, 이는 매우 자연스러운 현상이다
- 사용할 수 있는 리팩토링 기술
- 함수 선언 변경하기(Change Function Declaration)
- 변수 이름 바꾸기(Rename Variable)
- 필드 이름 바꾸기(Rename Field)
함수 선언 변경하기(Change Function Declaration)
- 좋은 이름을 가진 함수는 함수가 어떻게 구현되었는지 코드를 보지 않아도 이름만 보고도 이해 할 수 있음
- 구현부를 보지 않고도 선언부만 보고 이해 가능해야 함
- 좋은 이름을 찾아내는 방법: 함수에 주석을 달고, 주석을 함수 이름으로 만들어 보기
- 함수의 매개변수는
- 함수 내부의 문맥을 결정
- ex) 전화번호 포매팅 함수가 있다고 하면, 이 함수에 딱 '전화번호 텍스트'만 넘겨줄 것인지, 전화번호를 갖는 'Person'이라는 객체를 넘겨줄 것인지에 따라 함수 내부 문맥이 달라짐
- 전화번호만 넘겨주는 경우 다른 곳에서 재사용하기는 편해지지만 기능은 단순해짐
- Person을 넘기는 경우 해당 Person의 국가 정보를 참고해서 국가에 맞는 포매팅으로 전화번호를 변경해주는 것과 같은 기능이 가능해지지만 재사용성이 떨어짐
- 의존성을 결정
- ex) Payment 만기일 계산 함수가 있다고 할 때, Payment를 넘겨줄 수도 있고 Payment의 dueDate만 넘겨줄 수도 있다.
- Payment 만기일 계산 시 Payment가 가지고 있는 타입이나 여러가지 속성들을 참조해야 된다면 Payment라는 타입을 넘겨주는 것이 맞을 것
- dueDate만 필요하다면 dueDate만 넘겨주는 것이 맞을 것
- => 정답은 없음
- 함수 내부의 문맥을 결정
public class StudyDashboard {
private Set<String> usernames = new HashSet<>();
private Set<String> reviews = new HashSet<>();
/**
* 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어온다
* @param issue
* @throws IOException
*/
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 = GitHub.connect();
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);
}
}
위 코드에서 studyReviews
는 주석의 내용처럼 깃허브의 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어오는 기능을 하는 메서드이다. 기능을 생각했을 때, studyReviews라는 이름은 적절하지 않아보이므로 기능을 잘 반영할 수 있도록 loadReviews
로 변경한다. (기능만 잘 반영하고 있다면 이름은 자유)
이번엔 파라미터에 대해 고민해보자.
loadReviews는 현재 GHIssue라는 깃허브 이슈 객체를 받고 있다. 하지만, 잘 생각해보면 리뷰를 해올 이슈는 "whiteship/live-study"라는 레포지토리의 30번 이슈로 이미 정해져 있고 바뀌지 않는다. 즉, 이를 따로 넘겨줄 필요가 없다.
이렇게 수정된 결과는 다음과 같다.
private void loadReviews() throws IOException {
GitHub gitHub = GitHub.connect();
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());
reviews.add(comment.getBody());
}
}
변수 이름 바꾸기(Rename Variable)
- 더 많이 사용되는 변수일수록 그 이름이 더 중요함
- 람다식에서 사용하는 변수 vs. 함수의 매개변수
- 다이나믹 타입을 지원하는 언어에서는 타입을 이름에 넣기도 함
- 여러 함수에 걸쳐 쓰이는 필드 이름에느 더 많이 고민하고 이름을 짓는다
- 람다식에서 사용되는 변수의 경우
- 람다식은 굉장히 범위가 좁고, 그 안에 어떤게 들어있는지 이미 알고있는 경우가 많다
- 때문에, 간추려서 쓰거나 한 글자로 줄여서 쓰기도 한다
- 혹은, 변수 정의 없이 메서드 레퍼런스 형태를 사용하는 것도 바람직하다
- 변수는 그 문맥에서 변수의 역할을 제대로 반영하는 이름을 가져야 한다
public class StudyDashboard {
private Set<String> usernames = new HashSet<>();
private Set<String> reviews = new HashSet<>();
/**
* 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어옵니다.
* @throws IOException
*/
private void loadReviews() throws IOException {
GitHub gitHub = GitHub.connect();
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));
}
}
위와 같은 코드가 있다고 했을 때, main 함수 내부의 람다식은 모두 메서드 레퍼런스로 대체하는 것이 바람직해보인다. 또한, loadReviews 함수 내에서 comments라는 변수는 comment가 맞긴하지만, 좀더 명확하게 의미를 보여줄 수 있도록 하기 위해 reviews라는 이름으로 대체하는 것이 더 적절해보인다.
public class StudyDashboard {
private Set<String> usernames = new HashSet<>();
private Set<String> reviews = new HashSet<>();
/**
* 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어옵니다.
* @throws IOException
*/
private void loadReviews() throws IOException {
GitHub gitHub = GitHub.connect();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
GHIssue issue = repository.getIssue(30);
List<GHIssueComment> reviews = issue.getComments(); // 이름 변경
for (GHIssueComment comment : reviews) {
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(System.out::println); // 메서드 레퍼런스
studyDashboard.getReviews().forEach(System.out::println); // 메서드 레퍼런스
}
}
필드 이름 바꾸기(Rename Field)
- Record 자료 구조의 필드 이름은 프로그램 전반에 걸쳐 참조될 수 있기 때문에 매우 중요
- Record 자료 구조: 특정 데이터와 관련있는 필드를 묶어놓은 자료 구조
- ex) 파이썬의 Dictionary, 또는 줄여서 dicts / C#의 Record
- 자바 14버전부터 지원
- 자바에서는 getter와 setter 메서드 이름도 필드의 이름과 비슷하게 간주할 수 있음
- Record 자료 구조: 특정 데이터와 관련있는 필드를 묶어놓은 자료 구조
이전 코드를 다시 보자.
public class StudyDashboard {
private Set<String> usernames = new HashSet<>();
private Set<String> reviews = new HashSet<>();
/**
* 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어옵니다.
* @throws IOException
*/
private void loadReviews() throws IOException {
GitHub gitHub = GitHub.connect();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
GHIssue issue = repository.getIssue(30);
List<GHIssueComment> reviews = issue.getComments();
for (GHIssueComment review : reviews) {
usernames.add(review.getUserName());
this.reviews.add(review.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(System.out::println);
studyDashboard.getReviews().forEach(System.out::println);
}
}
현재는 리뷰를 하는 유저의 이름들과 리뷰 내용이 서로 다른 Set으로 관리되고 있다. 이를 레코드를 사용하여 함께 관리하도록 변경해보자.
public record StudyReview(String reviewer, String review) {
}
public class StudyDashboard {
private Set<StudyReview> studyReviews = new HashSet<>();
/**
* 스터디 리뷰 이슈에 작성되어 있는 리뷰어 목록과 리뷰를 읽어옵니다.
* @throws IOException
*/
private void loadReviews() throws IOException {
GitHub gitHub = GitHub.connect();
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);
}
}
728x90
반응형
'ETC' 카테고리의 다른 글
[Refactoring] 3. 긴 함수 (2) | 2025.01.02 |
---|---|
[Refactoring] 2. 중복 코드 (2) | 2025.01.02 |
[오브젝트 - 기초편] 변경과 설계 (2) | 2024.12.31 |
[오브젝트 - 기초편] 책임 할당하기 (2) | 2024.12.30 |
[오브젝트 - 기초편] 객체지향 기본 원칙 (2) | 2024.12.30 |