이전글에 이어 우아한테크코스에서 요구하는 클린코드 원칙들을 살펴본다.
검색을 하면서 이 규칙들이 <소트웍스 앤솔러지 : 소프트웨어 기술과 혁신에 관한 에세이, 마틴 파울러 수트웍스 지음, 옮긴이 다수, 위키북스> 의 책에서 주장된 내용이라는 것을 확인했다.
2009년에 나온 책으로 아쉽게도 동네 도서관들에서는 발견할 수가 없었다.
한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
이와 같은 규칙의 근거는 책에 따르면 다음과 같다.
... 거대한 메서드는 응집력이 떨어진다. ... 코드가 500줄짜리 괴물들로 어질러져 있으면 의욕이 꺾이기 쉽다. ... 한 메서드 안에 중첩된 제어구조가 있다면 다단계의 추상화를 코드로 짠 것이며, 고로 한 가지 일을 하고 있다는 뜻이다.
그러니까 말하자면
1) 긴 코드는 응집력이 떨어짐
2) 중첩된 제어구조는 긴 코드일 확률이 높음
3) 한 메서드는 한 가지 일을 하는 것이 바람직함
4) 짧을 수록 그 목표 성취가 가능함
이를 위해서 라는 것이다.
정확히 한 가지 일을 하는 메서드들로 작업을 하면 코드가 달라지기 시작한다. 애플리케이션의 각 단위가 더 작아짐에 따라 재사용의 수준은 기하급수적으로 상승하기 시작한다. ... 주어진 컨텍스트에서 단일 객체의 상태를 관리하는 3줄짜리 메서드라면 많은 다양한 컨텍스트에서 쓸 수 있다.
메서드가 한 가지 일을 맡아서 하게 되면 짧아지는 효과로 인해 가독성이 좋아지는 것은 물론 유지 보수에도 수월해진다.
오직 한 단계의 들여쓰기란 어떤 것인가 하면 다음과 같다.
책에서 제시된 예시이다.
< 원칙을 벗어난 상황>
class Board {
...
String board() {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++)
buf.append(data[i][j]);
buf.append("\n");
}
return buf.toString();
}
}
<리팩토링하여 원칙을 지킨 상황>
class Board {
...
String board() {
StringBuffer buf = new StringBuffer();
collectRows(buf);
return buf.toString();
}
void collectRows(StringBuffer buf) {
for (int i = 0; i < 10; i++)
collectRow(buf, i);
}
void collectRow(StringBuffer buf, int row) {
for (int i = 0; i < 10; i++)
buf.append(data[row][i]);
buf.append("\n");
}
}
이와 같은 리팩토링으로 인덴트를 1단계만 남기면 가독성이 좋아지고 기능이 분리되는 것을 볼 수 있다.
작성한 코드 중에 아래와 같이 indent depth가 중첩되어 있는 코드가 있다.
public static String solution(String cryptogram) {
String answer;
String regex = "(\\w)(\\1+)";
Pattern patternRegex = Pattern.compile(regex);
while (true) {
Matcher patternMatcher = patternRegex.matcher(cryptogram);
StringBuilder stringBuilder = new StringBuilder();
while (patternMatcher.find()) {
patternMatcher.appendReplacement(stringBuilder, "");
}
patternMatcher.appendTail(stringBuilder);
cryptogram = String.valueOf(stringBuilder);
System.out.println(cryptogram);
patternMatcher = patternRegex.matcher(cryptogram);
if (!patternMatcher.find()) {
answer = cryptogram;
break;
}
}
return answer;
}
이 코드의 로직은
1. regex 패턴을 만들고
2. 패턴과 검증할 문자열을 비교하여 찾고
3. addReplacement() 함수를 통해 제거한 후 stringBuilder를 통해 재구축을 하고
4. while문이 끝난 이후 마지막 tail 처리를 위해 한 번 더 stringBuilder로 구축을 해주고
5. 마지막으로 string 변환을 해주고
6. 여전히 중복이 있으면 계속 반복이 돌아가면서 없을 때까지 반복하는
로직이다.
여기서 중복 제거의 로직은 별도의 메소드를 만들어 다음과 같이 리팩토링 할 수 있을 것이다.
private static String removeDuplication(Matcher patternMatcher) {
String cryptogram;
StringBuilder stringBuilder = new StringBuilder();
while (patternMatcher.find()) {
patternMatcher.appendReplacement(stringBuilder, "");
}
patternMatcher.appendTail(stringBuilder);
cryptogram = String.valueOf(stringBuilder);
return cryptogram;
}
그러면 다음으로 while문을 끝내줄 조건을 설정하는 if문이 2단계의 depth로 존재한다.
이 부분은 메소드로 빼줄 수가 없는데 break을 end point를 while문 안에서 써주어야 반복을 종료시킬 수 있기 때문이다.
. . .
if 문을 없애고 break 시킬 수 있는 다른 방법을 고민중인데
본질적으로 loop 안에서 끊어주는 조건을 설정하고 break를 해주어야 탈출이 가능한 상황이다.
. . .
다른 방법을 찾아보면서 메서드를 일단 나누어 보았고
if로 break을 처리하는 다른 방법은 찾지 못해 1줄로 처리해보았다.
public static String solution(String cryptogram) {
String answer;
Matcher patternMatcher;
while (true) {
boolean doesDuplicationExists = checkDuplication(cryptogram);
patternMatcher = setMatcher(cryptogram);
cryptogram = removeDuplication(patternMatcher);
if (!doesDuplicationExists) { break; }
}
answer = cryptogram;
return answer;
}
'Programming > Java, Spring' 카테고리의 다른 글
모든 원시값과 문자열을 포장했는가? - 우아한테크코스 우테코 클린코드 #4 (0) | 2022.10.30 |
---|---|
else 예약어를 쓰지 않았는가? - 우아한테크코스 우테코 클린코드 #3 (0) | 2022.10.30 |
자바 코딩 컨벤션 - 우아한테크코스 우테코 클린코드 #1 (0) | 2022.10.30 |
[Java/Spring] 스프링 DB 매니지먼트 변천사 / JDBC, JPA, 스프링 데이터 JPA (0) | 2022.10.25 |
[Java/Spring] 스프링 빈과 의존관계 설정 (0) | 2022.10.24 |