메소드 추출하고, MVC 패턴 정의하고, else 안쓰게 노력해보는 앞 리팩토링과 동일한 목표를 수립했다🐳
[ 기능 ]
- [ ] 사용자에게 word를 입력받기
- [ ] 각 character를 로직에 맞게 변환하기
- 알파벳은 표 그대로
- 대문자는 대문자로
- 소문자는 소문자로
- 알파벳 외 문자는 변환 안함
[ 예외 ]
- [ ] word가 1 이상 1,000 이하인 문자열이 아닌 경우
아직도 MVC 패턴은 넘 어렵다 😂 해당 글을 보면서, Controller가 적재적소에 Service를 호출한다는 것을 알게 되었는데 어느 단위까지 Service
로 나눠야하는지 감이 잘 잡히지 않았다. 모델을 건드리는 코드들을 서비스에 몰아넣으면 되는것인가?
wordService
에서 규칙에 따라 바뀐 Character를 만들어주는 작업을 할까? 했는데
이것도 맞지 않는 것 같았다.
나는 이번 미션에 일급 컬렉션을 적용하고 싶어서, List<Character
를 갖고 있는 Word
엔티티를 도입했는데, 이 컬렉션을 다루는 로직을 service
에서 만들자니 일급 컬렉션의 목표에도 어긋났고, 그리고 로직도 더 복잡해졌기 때문이다. ㅠㅠ
그래서 이번 리팩토링에선 Word 엔티티에 역할을 밀어넣었다.
(다른 분들의 미션 레포지토리를 찾아봤는데, 그냥 정직하게 model
,view
,controller
만 사용하신 경우도 많길래 용기를 갖고!)
처음에 구현한 Word
클래스는 아래와 같다.
package onboarding.problem4.model;
import onboarding.problem4.util.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Word {
List<Character> words;
List<Character> changedWords;
public Word(String words) {
Validator.validateWords(words);
this.words = stringToCharList(words);
this.changedWords = new ArrayList<>();
}
private List<Character> stringToCharList(String input) {
return input.chars()
.mapToObj(e -> (char) e)
.collect(Collectors.toList());
}
public void updateChangeWords() {
for (Character c : words) {
this.changedWords.add(changeCharacter(c));
}
}
private Character changeCharacter(Character c) {
if (Character.isAlphabetic(c) ) {
return changeAlphabeticCharacter(c);
}
return c;
}
private Character changeAlphabeticCharacter(Character c){
if(Character.isLowerCase(c)){
return (char) (219-c);
}
return (char) (155-c);
}
@Override
public String toString() {
return changedWords.stream()
.map(String::valueOf)
.collect(Collectors.joining());
}
}
근데 이렇게 구현하니까, 테스트 코드에서 아래와 같이 예외가 발생했다.
Character는 "가"와 같은 한글(?)이 저장이 안되기 때문에 발생하는 예외였다!
생각하지도 못한 부분이었다. 아마 미션 할 당시에도 고려하지 못한 부분이지 않았나 싶다😂
문제 정의에는 영어가 아닌 다른 글자는 바꾸지 않는다고 명시되어 있기 때문에, 가,나
와 같은 타입의 입력이 들어와도 예외 상황은 아니라는 생각이 들었다.
따라서 로직을 변경했다! String
을 관리할 수 있도록!
package onboarding.problem4.model;
import onboarding.problem4.util.Validator;
import java.util.List;
import java.util.regex.Pattern;
import static onboarding.problem4.util.Constants.*;
public class Word {
List<String> words;
public Word(String words) {
Validator.validateWords(words);
this.words = List.of(words.split(""));
}
public String getChangedWord() {
StringBuilder sb = new StringBuilder();
for (String word : words) {
sb.append(getChangedCharacter(word));
}
return sb.toString();
}
private String getChangedCharacter(String input) {
if (isKorean(input)) {
return input;
}
char inputChar = input.charAt(0);
return changeCharacter(inputChar).toString();
}
private boolean isKorean(String input) {
return Pattern.matches(KOREAN_REGEX, input);
}
private Character changeCharacter(Character c) {
if (Character.isAlphabetic(c)) {
return changeAlphabeticCharacter(c);
}
return c;
}
private Character changeAlphabeticCharacter(Character c) {
if (Character.isLowerCase(c)) {
return (char) (LOWER_CASE_CHANGE_VALUE - c);
}
return (char) (UPPER_CASE_CHANGE_VALUE - c);
}
}
List<String
으로 문자를 관리하면서,
변경된 Word를 내뱉은 getChangedWord()
로직에서 한국어는 그대로 return(한국어 판단에는 정규식을 사용했다.), 영어면 변경한 값 return, 나머지는 그대로 return 하도록 구현하였다.
한국어 판별 로직을 가장 먼저 둔 이유는,
Character
클래스의 isAlphabetic()
을 활용하고 싶었는데 그러려면 한국어를 앞에서 미리 걸러서 character로 변환하지 않도록 만들어야했기 때문이다!
필요하다고 생각될 시, 한 번 적용해봐야겠다 :)
이 글에서 힌트를 얻었다. :) 클래스 필드의 상태?를 넘겨줄 타이밍엔 to_string()을 오버라이딩 해서 사용해보자!