의존성 분리를 통해 설계 개선하기 1편 (우아한객체지향)

의존성


두 클래스 A, B가 있을 때 어느 한 쪽이 변화가 생길 때 다른 한 쪽도 함께 변경될 가능성이 있는 경우를 의존성이 있다고 말한다.

여기서 변경이란
클래스의 이름 변경, 메서드의 이름 변경, 메서드의 구현 변경 등을 의미한다.

의존에는 클래스 간 의존성과 패키지 간 의존성이 존재한다.

클래스 의존성의 종류

연관 관계 Association

1
2
3
class A {
private B b;
}

A에서 B로 연결되어 있는 관계를 의미한다. 쉽게 말해 A에서 B의 객체 참조가 있는 경우를 의미한다. A라는 클래스에 B로 가는 영구적인 연결 통로가 생긴다.

의존 관계 Dependency

1
2
3
4
5
class A {
public B method(B b) {
return new B();
}
}

코드 상에서 인자 혹은 리턴 값으로 해당 타입을 사용하거나 메서드 안에서 해당 인스턴스를 생성하면 의존 관계이다. 이 관계는 해당 메서드를 사용할 때 일시적으로 맺어지는 관계이다. (영구적인 관계인 연관 관계와 다르다.)

상속 관계 Inheritance

1
class A extends B { ... }

상속 관계는 B 클래스의 구현을 그대로 가져와서 재사용하는 방식이다. 그래서 B 클래스의 구현이 변경되면 해당 구현을 사용하고 있던 A 클래스에 영향이 생긴다.

실체화 관계 Realization

1
class A implements B { ... }

실체화 관계는 인터페이스 B의 시그니처만 가져와서 사용하는 방식이다. 상속 관계와는 다르게 시그니처가 변경되지 않는 한 A 클래스에 영향이 생기지 않는다.

패키지 의존성

패키지 의존성은 A 패키지의 클래스 중 어느 하나라도 B 패키지의 클래스에 의존하는 경우 A 패키지가 B 패키지에 의존함을 의미한다.

좋은 설계를 위한 의존성 규칙

양방향 의존성을 피하라

양방향 Bi-Directional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A {
private B b;

public void setB(B b) {
if(b != null) { // 동기화
this.b.setA(null);
}
this.b = b;
this.b.setA(this); // 동기화
}
}

class B {
private A a;

public void setA(A a) {
if (a != null) { // 동기화
this.a.setB(null);
}
this.A = a;
this.A.setB(this); // 동기화
}
}

A와 B가 서로 의존한다는 의미는 A가 변할 때 B가 변할 수 있고, B가 변할 때 A가 변할 수 있다는 의미이다. 이런 경우 원래 이 두 클래스가 하나의 클래스로 묶어야 될 수 있다.

양방향 의존 관계에서는 setter 메서드를 사용할 때 동기화 해줘야 한다.

동기화를 왜 해줘야 하는 지 코드로 간략하게 알아보자.
만약 A와 B에서 주석으로 동기화라고 적혀 있는 코드가 없는 상태로 setter를 사용한다고 가정해보자.

1
2
3
4
5
6
7
8
9
A a1 = new A();
B b1 = new B();
B b2 = new B();
// 1. 서로 양방향 의존하도록 설정.
a1.setB(b1);
b1.setA(a1);

// 2. 이때 의존하는 인스턴스를 다른 인스턴스로 바꾸고 싶으면?
a1.setB(b1); // 이렇게 되면 b2는 a1에 연결되지 않은 상태다!!!! 그리고 심지어 b1은 아직 의존하고 있게 된다.

이렇게 양방향은 고려해야 할 부분이 많고 예상하지 못한 버그가 발생할 수 있다.

다중성이 적은 방향을 선택하라.

즉 일대다 대신 다대일 관계를 선택하는 게 좋다. 일대다를 JPA를 사용하다보면 N+1 문제에서 큰 성능 저하를 일으키게 된다.

1
2
3
4
5
6
7
8
9
// 일대다 관계 (비추!)
class A1 {
private Collection<B> bs;
}

// 다대일 관계 (추천!)
class A2 {
private B b;
}

패키지 사이의 의존성 사이클을 제거하라

의존성 사이클은 여러 패키지의 의존성 방향이 결국 연결되는 것을 의미한다. 크게 보면 결국 양방향 의존인 셈이다. 패키지가 양방향 의존하게 되면 하나가 바뀌면 다같이 바뀌게 될 가능성이 존재한다는 의미이다. 즉 원래 같이 묶여야 될 패키지라는 의미일 수 있다.

구체적인 실습은 2편에서 해보자!

Share