0. Overview
프로젝트 설계에 있어 객체의 구성 및 도메인 모델을 어떻게 구성해야할지에 대해 고민을 하게 되었습니다. 이때 기본이 되는 것이 UML, 특히 클래스 다이어그램(Class Diagram)이라는 것을 알게 되었고 대략적인 지식은 있었지만 보다 이를 명확하게 이해하기 위해 클래스 다이어그램에 대해 정리해보고자 합니다.
1. UML 이란 ?
먼저 UML이란 Unified Modeling Language의 약자로 도메인(해결하고자 하는 목표, ex) 예약 시스템 등)을 모델로 표현해주는 대표적인 모델링 언어로 알려져 있습니다. 이런 UML은 소프트웨어를 설계하고 필요에 의해 사용되는데 일반적으로 3가지의 목적을 가지고 만듭니다.
- 의사소통 또는 설계 논의
- 전체 시스템의 구조 및 클래스의 의존성 파악
- 유지보수를 위한 설계의 Back-end 문서 제작
2. 클래스 다이어그램(Class Diagram)
클래스 다이어그램은 정적 다이어그램으로 UML의 한 종류입니다. 클래스의 구성요소 및 클래스 간의 관계를 표현하는 대표적인 UML 이에요. 시스템의 일부 또는 전체의 구조를 나타낼 수 있고 의존관계를 명확히 보게 해줌과 동시에 순환 의존이 발생하는 지점을 찾아내어 어떻게 순환고리를 깰 수 있을지 결정할 수 있게 해줍니다.
[ 클래스 다이어그램의 2가지 용도 ]
1. 문제 해결을 위한 도메인 구조를 나타내 보이지 않는 도메인 안의 개념과 같은 추상적인 개념을 기술
2. 소프트웨어 설계 혹은 완성된 소프트웨어 구현 설명 목적
2 - 1. 클래스 다이어그램의 기본 요소
클래스 다이어그램을 그리기 위한 기본 요소에 대해 알아보도록 할게요. 클래스에는 클래스의 이름, 속성, 그리고 메소드가 존재하죠 ? 이런 요소들을 어떻게 클래스 다이어그램에 표현할 수 있을까요 ?
[ 접근제어자 리스트 ]
- + : public
- - : private
- # : protected
[ 형식 ]
- 속성(Attribute)
- 접근제어자 이름: 타입 = 기본값
- ex) #content: String = ""
[ 메소드 ]
- 접근제어자 이름(파라미터 속성) : 리턴값
- ex) +setContent(String)
- ex) +getContent():String
[ interface와 abstract(추상화) ]
- <<interface>>
- <<abstract>>
2 - 2. 클래스 다이어그램을 이용한 관계 표현
클래스 간의 연관관계는 위의 7가지로 표현할 수 있습니다. 각각이 어떤 관계를 의미하고 클래스 다이어그램으로 어떻게 표현되는지 알아볼게요 !
Generalization (일반화)
일반적으로 우리가 알고 있는 상속을 의미합니다. 이는 클래스 다이어그램에서 실선에 비어 있는 화살표로 표현해요 !
이런 도메인을 코드로 구현하면 다음과 같이 작성할 수 있습니다.
public class Person {
private String name;
private String phoneNumber;
public void eat() {
...
}
public void sleep() {
...
}
}
--------------------------------------------------------------------------------------
public class Student extends Person {
private String school;
private String studentNumber;
public void study() {
...
}
}
Realization (실체화)
Realization은 interface에 있는 spec을 오버라이딩하여 실제로 구현하는 것을 말합니다. 클래스 다이어그램에서 이를 점선과 비어있는 화살표로 표현해요 !
이런 도메인을 코드로 구현하면 다음과 같이 작성할 수 있습니다.
public interface Flyable {
void fly();
}
public class Plane implements Flyable {
... 필드
@Override
public void fly() {
System.out.println("Fly Plane !!");
}
}
Dependency (의존)
Dependency는 클래스간의 참조가 일어나는 것 중 하나입니다. Dependency 참조는 메소드 내에서 대상 클래스의 객체를 생성하거나 사용, 리턴받아 사용하는 것을 말합니다. 그리고 이 참조는 해당 클래스와의 관계를 계속 유지하지 않아요. Dependency는 점선과 화살표로 이루어져 있습니다.
이런 도메인을 코드로 구현하면 다음과 같이 작성할 수 있습니다.
public class Student {
private String school;
private String studentNumber;
public void study() {
...
}
public int getScoreRank(Ranking ranking){
return "해당 점수의 등수는 " + ranking.getRank() + "입니다.";
}
}
public class Ranking {
private int rank;
public int getRank(){
return rank;
}
}
Dependency는 메소드의 호출이 끝나고나면 연관된 클래스와의 관계가 마무리된다는 것이 중요해요 !
Association & Direction Association (연관)
클래스 다이어그램에서의 Association은 다른 객체의 참조를 가지는 필드를 의미합니다. 실선으로 표현되며 방향성이 존재하는 경우에는 화살표를 넣어서 표시합니다. 그리고 둘의 연관관계가 어떻게 되는지 숫자로 표현해요 !
연관관계의 숫자 표현은 아래와 같이 할 수 있습니다.
- 1 : 1개의 표현
- * : 0 ~ n 개의 표현
- n...m : n부터 m까지 연관관계를 맺음
아래 예제는 양방향 연관관계를 가지며 1:n의 관계를 표시한 다이어그램 입니다.
이런 도메인을 코드로 구현하면 다음과 같이 작성할 수 있습니다.
public class Board {
private List<Comment> comments;
public void addComment(Comment comment) {
comments.add(comment);
}
}
public class Comment {
private Board board;
public void setBoard(Board board) {
this.board = board;
board.addComment(this);
}
}
Composition (합성)
클래스 간 사이의 강력한 의존관계가 있어 하나가 삭제되었을 때 다른 하나가 존재할 수 없다면, 이는 합성(Composition)관계라고 합니다. 합성은 포함관계로 설명되기도 하는데 방은 집에 포함되어 있다(belongs-to)고 얘기할 수도 있고, 집은 방을 가지고 있다(has-a)고 얘기할 수도 있겠죠 ?
위 다이어그램을 살펴보면 Person 쪽에 속이 채워진 마름모가 있는게 보일텐데 해당 기호가 합성(Composition)의 기호입니다. 더 큰 범위쪽에 마름모를 표시한다고 생각하면 이해하기 수월해요 ! 위의 그림에서 모델러는 Person이 삭제된다면 Head, Body, Leg 도 존재하지 않는다고 가정하겠습니다.
이걸 자바 코드에서 표현하려면 Composition과 같이 has-a 관계는 private 변수를 사용해 나타냅니다.
public class Person {
private Head head;
private Body body;
private List<Leg> legs;
public Person() {
super();
this.head = new Head();
this.body = new Body();
this.legs = Arrays.asList(new Leg(), new Leg());
}
public void growUp() {
head.growUp(1);
body.growUp(2);
legs.stream()
.forEach(leg -> leg.growUp(5));
}
public void checkLengths() {
System.out.println(head);
System.out.println(body);
legs.stream()
.forEach(System.out::println);
}
}
Aggregation (집합)
집합(Aggregation) 관계는 A 클래스가 B를 소유하고 있다거나, A 클래스가 B의 부모라거나 하는 것을 가리키는것이 아닙니다. A 클래스의 인스턴스는 B 클래스의 인스턴스와 배타적이지(독립적이지) 않다는 것을 강조하기 위한 것입니다.
위 다이어그램에서 Engine과 Wheel은 Car와 집합관계이지만 차가 없어진다고 해서 Engine과 Wheel이 삭제된다고 보진 않았습니다.
Aggregation을 코드로 표현한다면, 참조만 하도록 하면 됩니다. 다른 바퀴나 엔진으로 교체될 수 있으며 생명주기가 동기화될 필요가 없습니다 !
public class Car {
Engine engine;
List<Wheel> wheels;
}
3. 연관과의 관계
예제를 통해 다소 헷갈릴 수 있는 합성과, 집합 관계를 이해하셨나요 ?
그렇다면, 집합(Aggregation)과 합성(Composition)이 연관(Association)과 어떻게 관련이 있을까요 ?
연관이란, 다른 클래스에서 제공하는 기능을 사용하는것을 나타내는 일반적인 용어라고 할 수 있습니다. 때문에 집합(Aggregation)과 합성(Compositon)은 연관의 특수한 부분집합이라고 할 수 있어요 !
🔔 또한 소스코드 상으로 연관(Association)은 집합(Aggregation)과 구분할 수 없습니다.
<< 참고 자료 >>