service가 view에 의존하지 않도록 하자

공병주(Chris)·2022년 4월 12일
1

Java를 탄탄히

목록 보기
8/9

의존한다가 뭐야?

먼저 의존에 대해서 정의를 해야할 것 같습니다. 다들 각자의 정의가 있겠지만, 저는 의존에 대해서 아래와 같이 생각합니다.

A가 변경되면 B에 변경이 일어난다 → A는 B에 의존한다.

객체들은 자신의 책임을 수행하기 위해서, 다른 객체에게 어떤 것을 요청하고 요청에 대한 값을 응답을 받습니다.
A 객체가 B 객체에게 요청을 보내서 a형태의 응답을 받는다고 생각해보자. 응답의 형태는 B 객체에서 정해집니다.
따라서, B 객체에서 응답의 형태가 변경이 된다면, A 객체에서도 변경이 일어납니다.

public class A {

		public void method() {
				final B b = new B();
				int something = b.findSomthing(); //아래의 코드로 변경이 일어남
				//double something = b.findSomething();
		}
}

public class B {

		public int findSomething() {
				//변경 전의 메소드
		}

		public double findSomthing() {
				//변경된 메소드
		}
}

위와 같이 import를 하면서 명확하게 들어나는 결합과 의존이 있지만,

보이지 않는 의존(혹은 결합)도 존재했습니다. (의존이라는 단어로 표현해도 될지...)

public class ChessGameService {

public Map<String, Object> move(final String sourcePosition, final String targetPosition) {
				Board board = loadSavedBoard();
				board = board.move(sourcePosition, targetPosition);
				final Map<Position, Piece> pieces = board.getAllPieces();
				return convert(pieces); 
				// spark 프레임워크에서 사용할 수 있는 Map<String, Object>로 변환
		}
}

위와 같이 ChessGameService에서 출력을 위해 기물에 대한 정보를 Map<String, Object>의 형태로 반환합니다.

Map<String, Object> 타입으로 반환을 하는 이유는 출력을 위한 SparkFramework에서 Map<String, Object> 형태의 자료구조를 통해 화면에 render를 하기 때문입니다.

이렇게 하면 어떤 문제가 있을까요?

출력 방식이 변경되면 해당 ChessGameService는 사용할 수 없고, 변경을 해줘야 합니다.

현재는 sparkFramework에서 Map<String, Object>의 형태가 필요하지만, 다른 자료구조 형태를 필요로 하는 것으로 view가 변경이 되었을 시에, ChessGameService는 더이상 사용할 수 없고 반환 타입을 변경해주어야겠죠.

위에서 의존을 변경에 대한 파급효과가 발생하는지로 정의했습니다.

그렇다면, 위의 service는 view에 의존한다고 말할 수 있습니다.

view와 Service 사이에 controller 레이어가 존재하기 때문에 view와 Service가 서로를 import하지 않지만,
Service의 입장에서 view가 필요로 하는 자료구조의 형태를 알기 때문에 view에 대해 구체적으로 안다고 생각합니다.

private void move(final ChessGameService chessGameService) {
    post("/move", ((req, res) -> {
        final Map<String, Object> pieces = chessGameService
                .move(req.queryParams("source"), req.queryParams("target"));

        return render(pieces);
    }));
}

또한 아래처럼, view가 변경되었을 경우에, Service와 View를 잇는 controller에서 변경된 view가 필요로 하는 형태로 값을 가공해줄 수 있습니다.

private void move(final ChessGameService chessGameService) {
    post("/move", ((req, res) -> {
        final Map<String, Object> pieces = chessGameService
                .move(req.queryParams("source"), req.queryParams("target"));

        return render(convertToNewViewFormat(pieces));
    }));
}

private Map<String, String> convertToNewViewFormat(final Map<String, Object> pieces) {
		//변경된 view에서 필요로 하는 형태로 가공
}

하지만 ChessGameService는 여전히 sparkFramework를 사용하는 view에 종속적입니다.

public class ChessGameService {

		public Map<String, Object> move(final String sourcePosition, final String targetPosition) {

		}
}
private void move(final ChessGameService chessGameService) {
        post("/move", ((req, res) -> {
            final Map<Position, Piece> pieces = chessGameService
                    .move(req.queryParams("source"), req.queryParams("target"));
						final Map<String, Object> convertedPieces = convertToWebViewPiece(pieces);
            return render(convertedPieces);
        }));
    }

Service에서 어느 view에 종속되지 않는 형태를 반환하고

아래 처럼, controller에서 view에 맞는 형태로 가공을 해줘야 service가 가지는 view에 대한 의존을 제거할 수 있습니다.

객체 들이 서로에 대해 너무 자세히 알면 변경에 대응하기 힘듭니다. 또한, 변경이 상대적으로 잦은 view에 대해 자세히 안다면 변경에 대응하기가 더 힘들겁니다.

객체들끼리 너무 친한 관계를 맺어주지 맙시다~!

끗.

profile
self-motivation

1개의 댓글

comment-user-thumbnail
2022년 4월 12일

잘 읽고 갑니다~!!👍

답글 달기