높은 응집도 낮은 결합도
1주 차의 핵심 키워드는 "높은 응집도 낮은 결합도"였다. 설계를 할 때도 항상 키워드를 중심으로 설계했다.
Keep
설계를 확실하게 한 뒤, 개발을 시작하니까 갑작스럽게 변하는 내용이 없어서 좋았다.
높은 응집도, 낮은 결합도를 중심으로 개발을 하다 보니 자연스레 MVC 패턴이 생각났고 문제에 적용시켜보았다.
설계
- Controller
- View에게 입력을 받은 뒤, Service를 통해 데이터의 가공을 요청하고 다시 결과를 View를 통해 표현
- View
- 입력을 받은 뒤, Controller에게 결과를 전달하고, 받은 결과를 출력한다.
- Sevice
- Controller을 통해 데이터 가공을 요청받은 뒤, 가공을 마치면 결과를 Controller로 전달.
이렇게 개념을 확실하게 공부한 뒤, 적용시키니 위의 사진과 같은 결과가 나오게 되었다.
2,3,4주 차 미션을 진행할 때도 문제를 이해한 뒤, 설계 후 개발을 해야겠다.
Problem
MVC패턴에 너무 집중한 결과 1주 차 미션에는 너무나도 많은 문제점들이 있었다. 😢
1. JDK 17을 효과적으로 활용하지 못했다.
대표적으로 record를 사용하지 않았다.
다른 분들의 코드를 확인해 보니 record를 사용한 분들이 많았다.
아무래도 프로젝트 및 개인 공부를 JDK 11로 진행하다 보니 이런 안일한 일이 일어난 것 같다.
2주 차 미션을 시작하기 전, record를 확실히 공부하고 블로그에 글을 작성해야겠다.
(너무나도 나에게 실망했다)
2. 의존성 주입
내가 구현한 코드를 보니 클래스에서 다른 클래스의 인스턴스를 직접 생성하고 있었다.
private final BaseballService baseballService = new BaseballService();
private final ValidationService validationService = new ValidationService();
private final ComputerService computerService = new ComputerService();
private final InputView inputView = new InputView();
private final OutputView outputView = new OutputView();
이렇게 코드를 작성하게 되면 결합도가 높아져 유지보수 하기가 어려워진다.
결국은 내가 1주 차의 핵심으로 생각했던 키워드를 위반하게 되었다.
3. Controller가 아닌 Model에서 View에게 값을 넘겨주었다.
public boolean startGame(String randomNumber, String input) {
int strike = countStrike(randomNumber, input);
int ball = countBall(randomNumber, input);
if (strike == 3) {
outputView.printThreeStrike(strike);
return false;
} else {
printResult(strike, (ball - strike));
return true;
}
}
문제의 코드다.
MVC 패턴에서는 Controller가 Model로부터 데이터를 받아 View에 전달하는 역할을 합니다. 그러나 저는 Model인 BaseballService가 직접 OutputView에 값을 전달하고 있습니다. 이렇게 하면 (SRP) 단일 책임 원칙을 위반하고 결합도를 높이는 단점이 있습니다.
* BaseballService는 비즈니스 로직을 처리하는 역할을 가지고 있지만, OutputView에 값을 전달하는 역할까지 수행하게 되어 단일 책임 원칙을 위반하였습니다.
Try
1. 클래스의 인스턴스를 생성자를 통해 주입한다.
public BaseballController(BaseballService baseballService, ValidationService validationService,
ComputerService computerService, InputView inputView, OutputView outputView) {
this.baseballService = baseballService;
this.validationService = validationService;
this.computerService = computerService;
this.inputView = inputView;
this.outputView = outputView;
}
Problem 2번의 의존성 주입에서 코드를 생성자를 통해 주입하는 방식으로 고쳐보았다.
이렇게 하게 된다면 순환 의존성을 컴파일 시점에 발견할 수 있으며, 의존성을 명시하고 불변성을 보장하므로 결합도를 낮출 수 있습니다.
2. 도메인 객체를 생성한다.
Problem 3번의 문제 같은 경우 Model에서 View를 통해 값을 전달한 것이 문제였다.
이를 해결하기 위해 Controller에서 변수를 생성한 뒤 View로 전해주면 되지 않을까?라는 생각을 해봤는데 장점에 비해 너무 단점이 많았다.
장점
1. 코드가 깔끔해진다.
단점
1. 데이터를 전달하기 위한 변수의 개수를 파악하기 어렵다.
2. 변수가 많은 경우 데이터 이동이 많아지므로 유지보수가 어렵다.
코드가 깔끔해진다는 장점이 있지만 유지보수가 얼마나 개발자에게 중요한지 알기에 나는 과감히 도메인 객체를 생성하기로 마음먹었다. 도메인 객체를 이용하면 데이터의 일관성을 유지할 수 있고, 객체를 재사용할 수 있어 중복을 줄일 수 있다.
다짐
디스코드 및 깃허브 코드 리뷰를 보니 정말 잘하시는 분들도 많았고 다들 열심히 하는 것 같았다.
그런 분들을 보니 나도 모르게 오기(?)가 생겼다.
1주 차 미션은 뒤에서 1등으로 제출했지만, 4주 차 미션은 수석으로 제출하고 싶다. (수준을 말하는 것입니다..!)