온보딩 problem4 리팩토링

MINJU·2022년 12월 11일
0
post-thumbnail

문제4

지난 PR 셀프 리뷰

메소드 추출하고, MVC 패턴 정의하고, else 안쓰게 노력해보는 앞 리팩토링과 동일한 목표를 수립했다🐳

기능 정의

[ 기능 ]
- [ ]  사용자에게 word를 입력받기
- [ ] 각 character를 로직에 맞게 변환하기
	- 알파벳은 표 그대로
    	- 대문자는 대문자로
    	- 소문자는 소문자로
	- 알파벳 외 문자는 변환 안함

[ 예외 ] 
- [ ] word가 1 이상 1,000 이하인 문자열이 아닌 경우

구현

아직도 MVC 패턴은 넘 어렵다 😂 해당 글을 보면서, Controller가 적재적소에 Service를 호출한다는 것을 알게 되었는데 어느 단위까지 Service로 나눠야하는지 감이 잘 잡히지 않았다. 모델을 건드리는 코드들을 서비스에 몰아넣으면 되는것인가?

wordService에서 규칙에 따라 바뀐 Character를 만들어주는 작업을 할까? 했는데
이것도 맞지 않는 것 같았다.

나는 이번 미션에 일급 컬렉션을 적용하고 싶어서, List<Character를 갖고 있는 Word 엔티티를 도입했는데, 이 컬렉션을 다루는 로직을 service에서 만들자니 일급 컬렉션의 목표에도 어긋났고, 그리고 로직도 더 복잡해졌기 때문이다. ㅠㅠ

그래서 이번 리팩토링에선 Word 엔티티에 역할을 밀어넣었다.

(다른 분들의 미션 레포지토리를 찾아봤는데, 그냥 정직하게 model,view,controller만 사용하신 경우도 많길래 용기를 갖고!)


Word 엔티티

처음에 구현한 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을 관리할 수 있도록!

변경한 Word 엔티티



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로 변환하지 않도록 만들어야했기 때문이다!



다음에 적용할 부분

  1. Service 클래스 도입?
    : 항상 Service 클래스의 역할이 너무 애매해서 도입하지 못했었는데, 해당 글의 아래와 같은 설명을 기반으로 한다면 조금 정의가 명확해질 것 같다는 생각이 들었다!

필요하다고 생각될 시, 한 번 적용해봐야겠다 :)

  1. to_string() 사용하기

이 글에서 힌트를 얻었다. :) 클래스 필드의 상태?를 넘겨줄 타이밍엔 to_string()을 오버라이딩 해서 사용해보자!

0개의 댓글