우아한테크코스에서 진행하는 미션을 혼자 진행해보면서 나의 생각과 배운 것을 정리하는 글

[페어매칭 미션]

[내코드PR]

 

[페어매칭] 미션 제출 by changjun6518 · Pull Request #1 · changjun6518/java-pairmatching-precourse

테스트 코드 작성 MVC 패턴 객체 메세지 전달 의존관계

github.com

 


Course enum에 여러 변수들?을 담았는데 책임이 많아져서 문제?

public enum Course {
    BACK_END("백엔드", "./src/main/resources/backend-crew.md"),
    FRONT_END("프론트", "./src/main/resources/frontend-crew.md"),
    ;

    static{
        for (Course course : Course.values()) {
            course.initCrews();
        }
    }

    private String name;
    private String filePath;
    private Crews crews;

    Course(String name, String filePath) {
        this.name = name;
        this.filePath = filePath;
    }

    private void initCrews() {
        crews = CrewFileReader.readCrews(filePath);
    }
}
  • Course 이름 , filePath, 크루 리스트 등 과정에 속해있는 필요한 요소들을 enum안으로 다 담았는데 적절한 방법으로 사용한 건지 모르겠다
  • static은 프로그램을 실행하자마자 데이터를 보유하고 있어하게끔 (캐쉬 역할을 하게) 함

Enum의 예외처리는 어떻게할까?

  • 이건 검증로직이 어디에 있을까에 대한 의문인 것 같은데 enum도 클래스처럼 안에 하는게 좋겠지?

백엔드 과정 및 프론트 과정 크루 리스트 받아올때 중복 코드 어떻게 제거함?

public class CrewFileReader {
    static List<String> crewsString = new ArrayList<>();

    public static Crews readCrews(String filePath) {
        try {
            BufferedReader bufferedReader = new BufferedReader(
                    new FileReader(filePath)
            );
            String str;
            Crews crews = new Crews();
            while ((str = bufferedReader.readLine()) != null) {
                crewsString.add(str);
                crews.add(str);
            }
            bufferedReader.close();
            return crews;
        } catch (Exception e) {
            throw new IllegalArgumentException("제대로 파일을 읽어올 수 없습니다!");
        }
    }
}
  • 인터페이스로 처리해보고 싶은데 잘 모르겠음
  • 매개변수로 파일경로 주입시켜줬음 -> 파일경로를 설정해주는 역할을 상위 객체로 넘김

이미 매칭되었는지 판단할때 map으로 확인하는데 String 값의 경우 객체기 때문에 포인터가 다를 것 같은데 contains를 사용했을때 같은 객체라고 판단할까?

private final HashMap<Level, List<String>> crewsByLevel;

public boolean isMatched(Level level, String crewName) {
    if (crewsByLevel.containsKey(level)) {
        List<String> matchedCrews = crewsByLevel.get(level);
        return matchedCrews.contains(crewName);
    }
    return true;
}
  • Heap 에 존재하는 객체들이기 때문에 같은 객체라고 판단할 것이다
  • 여러 군데에 쓰여도 같은 객체를 가리키고 있다
  • Key가 String이라면 equals 함수의 내부 로직이 있을거야...! 아마도

Matching - Crews - Crew 페어 매칭 이력 검사해서 이미 매칭되었는지 검증하는 함수 명을 똑같이 하는게 맞는가?

  • 3개 클래스 모두 매칭되었는지 검증하는 함수명이 isMatched인데 헷갈린다
  • 근데 의존관계?가 명확하면 그렇게 헷갈리지 않을 것 같기도함 -> 찾아보자

Matching 클래스를 불변객체? 로 두고 하나의 객체만 계속 사용하도록 만들면 공간효율이 좋지 않을까 생각함

public class Matching {

    private final List<String> stringCrews;

    public Matching(List<String> stringCrews) {
        this.stringCrews = stringCrews;
    }

    public MatchingResult match(Level level, Crews crews) {
        shuffle();
        MatchingResult matchingResult = new MatchingResult();
        matchingResult.match(stringCrews, level, crews);
        return matchingResult;
    }

    private void shuffle() {
        Collections.shuffle(stringCrews);
    }

}
  • 하지만 여러 매칭이 동시에 실행된다고 했을때 시간효율이 안좋음 + 멀티스레드 환경에서 동시 접근안됨

equals와 hashcode 재정의할때 왜 둘다해야할까?

public class MatchingInfo {
    private Course course;
    private Level level;
    private Mission mission;

    public MatchingInfo(Course course, Level level, Mission mission) {
        this.course = course;
        this.level = level;
        this.mission = mission;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || getClass() != o.getClass()) return false;
        MatchingInfo matchingInfo = (MatchingInfo) o;
        return course == matchingInfo.course &&
                level == matchingInfo.level && mission == matchingInfo.mission;
    }

    @Override
    public int hashCode() {
        return Objects.hash(course, level, mission);
    }
}
  • 해쉬코드는 객체의 주소값을 변환하여 생성한 객체의 고유한 정수값
  • 실제 객체들의 주소가 다르더라도 변환된 hashCode를 보고 같은 객체인지 판단함!
  • 그래서 String 같은 경우도 문자열이 같으면 hashCode가 같다

key 결과에 따라서 Dto 보다는 matchingResult 가 저장되었으면 하는데 service에서 가져오도록 하려면 로직을 전부 수정해야함 → 잘못된 코드 아닌가

private final HashMap<MatchingInfo, MatchingResult> repository = new HashMap<>();
  • 원래 MatchingResultDto 였는데 MatchingResult로 수정
  • service 에서 Dto를 반환받도록 되어있어서 결과값을 수정하려면 모두 다 변경해야함
  • 테스트코드를 작성하면서 진행했으면 좀 더 수월했을 것 같음!

Matching을 MatchingResult와 분리 시켰는데 MatchingResult public method 들이 불안함

  • public 메소드를 어쩔수 없이 사용하는 경우 접근 제한을 어떻게 할까?
  • 패키지로 접근 제한하나?

엔티티에서 Dto를 만들어서 반환할까? 아니면 service에서 get으로 엔티티 내부 변수 가져와서 Dto로 변환할까?

public MatchingResultDto convertDto() {
        return new MatchingResultDto(matchingResult);
    }
  • getter 안쓰려면 안에서 이렇게 해줘야할 것 같다

단위 테스트 진행할때 접근제어자 public에서 private으로 변경되는 경우는 단위 테스트 진행 못하나요? (matching에서 기능들 private으로 변경해서 이전에 테스트한 것들에 에러생김)

  • 굳이 private 까지 테스트를 진행할 필요가 없다고 한다...!
  • private를 할 수 있는 방법은 있다
  • 쓸데없는 테스트때문에 효율도 떨어지고 코드량도 많아지는 문제가 있을 것 같다

MatchingTest에서 단위 테스트 진행할때 공통되는 부분이 많다(given, when, then)

class MatchingServiceTest {

    @Test
    public void matchTest() {
        // given
        Course course = Course.BACK_END;
        Level level = Level.LEVEL1;
        Matching matching = new Matching(course.getStringCrews());
    }
}
  • 공통된 부분은 @Before 를 사용하여 setup해서 진행하자

회고

우테코 최종 코테였던 미션이다. 처음 풀때 엄청 어려웠는데 지금은 꽤나 잘 풀 수 있는 것 같다.

고민없이 공부하는 게 문제인 것 같고 어떤 방법이 더 좋은지 끊임없이 생각하고 적용해보면서 장단점을 체화하는게 좋은 것 같다. 더 나아가서 이유에 대해서 논리적으로 설명할 수 있으면 좋겠다.

끝.

+ Recent posts