최근에 개발 관련 서적들을 읽으면서 복잡한 부분은 클래스 다이어그램을 보고 해당 클래스들의 구조를 이해 할 수 없다는 것이 가장 큰 문제였다. 코드를 보면서 어느정도 되돌아가면서 이해를 할 수는 있었지만 너무나도 번거로웠다. 또 그러다보니 책을 읽는데에 있어서 집중력도 크게 저하됐다. 앞으로는 좀 더 수월한 독서를 위해 클래스 다이어그램을 한 번 공부를 제대로 하고 넘어가는 것이 좋을 것 같아 이번 기회에 정리를 하게 되었다.
클래스 다이어그램에는 여러가지 요소들이 존재한다 하나 하나 살펴보자.
클래스(Class)
클래스는 크게 3개의 구역으로 나누어 표시한다.
- 클래스 이름
- 속성 (필드)
- 기능 (메서드)
여기서 클래스 이름은 필수이고, 나머지 구역은 필수가 아니다. 속성과 기능의 구조에 대해서 살펴보기 전에 먼저 접근 제어자의 표현 방식부터 알아보자.
접근 제어자(Access Modifier)
- + : public
- - : private
- # : protected
접근 제어자는 위처럼 기호로 나타냄을 확인 할 수 있다. 이어서 속성과 기능(메서드)의 표현 방식을 살펴보자.
1. 속성(Attribute)
접근제어자 이름 : 리턴타입 기본값(생략가능)
ex) - title : String = ""
2. 기능(Method)
접근제어자 이름(파라미터 타입) : 리턴 타입(void 생략가능)
ex) + setTitle(String), + getTitle() : String
Stereo Type
기본적인 요소외에 추가적인 확장 요소, 보통 길러멧 기호(<< >>) 사이에 적는다.
위에 보면 이전에 보던 것과 다르게 특수한 것 2개가 추가로 보인다.
- 필드, 메서드 밑의 밑줄 : static 을 의미한다.
- {readOnly} : 수정이 되면 안된다는 final을 의미한다.
이 외에도 길러멧 키워드에는 다양한 것들을 사용하고는 한다.
ex) <<abstract>>, <<enumerate>>, <<annotation>> 등등
Abstract Class/Method
추상 클래스 혹은 메서드는 조금 다르게 표현이 가능하다. 추상 클래스의 표현 방법은 다음과 같이 3개로 사용할 수 있다.
- 추상 클래스의 이름과 메서드를 italic 체로 기울여서 표현한다. -> 주로 이걸 많이 사용하는 것으로 보인다.
- {abstract} 를 표시한다.
- << abstract >> 길러멧을 사용한다.
Class Relationship
해당 부분이 굉장히 중요하다. 클래스 다이어그램의 주 목적은 클래스 간의 관계와 의존 관계를 쉽게 파악하는 것이다.
따라서 이 부분은 여러 번 보고 읽어야 한다. 우선 전체적으로는 다음과 같은 관계로 구성 할 수 있다.
Association, Directed Association(연관 관계)
다른 객체의 참조를 가지고 있을 때 이러한 연관 관계를 사용하고, 방향이 있는 실선과 방향이 없는 실선 두 가지로 연관 관계를 나타낼 수 있다. 위 사진을 보면 닫히지 않는 화살표를 사용하는데, 어떤 교재에서는 Association을 안이 채워진 삼각형으로 표현하는 경우도 볼 수 있었다. 두 가지 경우 존재 할 수 있다고 생각해두고, 기본적으로는 위 방식이 옳다.
1. 방향이 있는 실선(Directed Association)
위 처럼 User -> Grade 처럼 방향이 있는 실선의 경우 User가 Grade를 참조한다는 의미이다.
방향이 있는 실선에 양 옆을 보면 추가적인 표현들이 보인다.
-phones는 연관 객체에서의 역할명(Role Name)을 나타내고 - 는 기존과 동일하게 접근 제어자를 의미한다.
*은 인스턴스의 개수의 범위를 뜻한다.
- 1 : 1개
- * : 0 ~ n 개
- n ... m : n부터 m까지 연관관계를 맺는다. 주로 1 ... n 으로 많이 사용한다.
2. 방향이 없는 실선(Association)
주로 양방향성의 연관관계를 표현할 때 사용하고, 보통은 객체 관계에서는 양방향성의 관계가 많아서 많이 사용하게 된다.
Generalization(일반화) - Inheritance (상속 관계)
부모 클래스와 자식 클래스의 상속 관계를 표현하는데 사용한다. 슈퍼 타입 서브 타입 관계라고 볼 수 있다.
일반화라고 부르는 이유는 우리가 자주 사용하는 용어로 바꾼다면 추상화과 구체화로 바꿀 수 있겠다. 구체화 된 자식 클래스들을 일반화(추상화) 하는 것이기 때문에, 일반화 라고 부른다. 표현 방식은 부모 방향으로 안이 비어있는 실선 삼각형을 사용하는 것을 볼 수 있다.
Realization(실체화) - Implements (구현)
interface의 추상 메서드들을 실제 기능으로 구현하는 것을 의미한다.
위 처럼 2가지 방식으로 표현이 가능하다고 하는데, 여러 교재에서는 2번째는 많이 사용을 하지 않고 첫 번째 기존의 클래스를 표현하는 방식을 많이 사용하는 것 처럼 보였다. 따라서 첫번째 방식을 사용하는 것이 더 좋을 것 같다는 생각이 든다.
표현 방식은 부모 방향으로 안이 비어있는 점선 삼각형을 사용하는 것을 볼 수 있다.
Dependency(의존 관계)
클래스 다이어그램에서 일반적으로 가장 많이 사용되는 관계라고 한다. 어떤 클래스가 다른 클래스를 참조하게 될 때 사용하는 방식인데 우리는 비슷한 것을 위에서 봤다. 바로 Association(연관 관계)이다.
Association VS Dependency
Association은 단순히 다른 클래스와의 연관 관계를 변수로 나타낼 때 사용하고, Dependency는 더 많은 방식으로 나타낼 수 있다.
Dependency의 참조 형태는 다음과 같다.
- 메서드 내 대상 클래스 객체 생성
- 메서드 내 대상 클래스 객체 리턴
- 메서드에서 대상 클래스 객체를 매개변수로 받아서 사용하는 경우
다만 Dependency 관계는 계속 유지되는 방식이 아니고 메서드의 호출이 시작될 때 관계가 이어졌다가 메서드의 호출이 끝나면 관계가 끝나는 형식이다.
Aggregation (Shared Aggregation, 집합)
Aggregation은 Shared Aggregation 이라고 한다. Association 관계를 조금 더 특수하게 나타낸 것으로 whole(전체)와 part(부분)의 관계를 나타낸다.
Aggregation은 위와 같은 관계를 나타낸다. Aggregation을 보면 솔직하게 Association과 큰 차이가 없어 보인다. 단지 집합으로서 표시한다는 것 뿐이다. 클린 코드의 저자(Robert C. Martin) 일명 밥 아저씨가 작성한 "UML 실전에서는 이거만 쓴다" 라는 책에서 UML 2.0에서는 위와 같은 이유로 해석상으로 구분하기 어려워서 Aggregation을 사용하지 않는다고 얘기를 한다. 따라서 위와 같은 관계가 존재한다고만 인지하고 위와 같은 방식을 표현할 때에는 웬만하면 Association 관계를 사용하자.
Composition (Composite Aggregation, 합성)
Aggregation과 비슷하게 전체(whole)와 부분(part)의 집합 관계를 나타낸다. 하지만 개념적으로는 Aggregation 보다 강한 집합을 의미한다. 위와 같은 합성 관계에서는 부분이 전체에 종속적이고 전체 부분이 part의 라이프 사이클을 책임집니다.
Aggregation vs Composition
- whole 인스턴스가 part 인스턴스를 생성한다.
- whole 인스턴스가 소멸하면 part 인스턴스도 함께 소멸한다.
- whole 인스턴스가 복사되면 part 인스턴스도 함께 복사한다.
- part에 해당하는 인스턴스는 공유될 수 없다.
위와 같이 강한 집합이기 때문에 개발자가 직접 구현해주어야 할 몇 가지 부분이 있습니다.
- part 인스턴스의 공유 방지를 위해서 Deep Copy 를 구현해야 합니다.
- whole 인스턴스가 part 인스턴스의 수명을 책임져야 하므로 whole 클래스의 생성자 또는 기타 메서드 내에서 part 인스턴스를 생성합니다.
- 외부에서 part 객체를 생성하지 못하도록 whole 클래스에는 part 인스턴스에 대한 setter를 삭제합니다.
- 자바는 Garbage Collector 가 객체 소멸을 담당하므로 part 인스턴스의 소멸은 신경쓰지 않아도 된다.
결론
공부를 하면서 다시 이전에 클래스 다이어그램이 나오던 책들을 몇 개를 살펴봤다. 객체들간의 관계가 이제는 한 눈에 파악이 되니 책을 읽는데에 있어서도 바로 바로 집중할 수 있고 그림이 그려지니 머릿속에서 상상하기도 쉬웠다. 향후 프로젝트를 진행하는데 있어서도 어떠한 행위를 처리하기 위한 메서드를 만들고 해당 메시지를 받는 객체에 대한 책임 관점에서 바라보면서 클래스 다이어그램을 작성 할 수 있게 되면 좀 더 좋은 설계로 프로젝트를 진행 할 수 있지 않을까 하는 생각이다!
참고 자료
https://brownbears.tistory.com/577
https://velog.io/@khyunjiee/UML-Class-Diagram
https://www.nextree.co.kr/p6753/
https://thinking-jmini.tistory.com/26
'Language' 카테고리의 다른 글
gradle build vs Intellij IDEA build (0) | 2023.05.20 |
---|---|
Lombok ToString 순환참조 방지하기 (1) | 2023.05.12 |
전략 패턴 (0) | 2023.04.16 |
템플릿 메서드 패턴 (2) | 2023.04.15 |