지난 글에서는 커뮤니케이션 다이어그램 대신 그릴 수 있는 시퀀스 다이어그램에 대해 정리하였다.
이번 글에서는 커뮤니케이션 다이어그램/시퀀스 다이어그램을 통해 그렸던 analysis class diagram 에 detailed design 을 추가하는 방법을 정리해본다.
클래스 다이어그램에 detailed design 을 추가하는 것은 다음 작업들을 하는 것이다.
1. 어트리뷰트 타입 추가
클래스 다이어그램에 어트리뷰트를 기술할 때는 다음과 같이 기술한다.
이름 : 타입 = 초기값 {속성}
속성에는 constant, fixed, not null 과 같은 특성을 기술한다.
속성 앞에 / 를 붙이면 상속받은 (derived) 속성을 나타내고, 밑줄을 그으면 클래스 변수 (static) 를 나타낸다.
배열의 경우 '이름[시작인덱스..끝인덱스] : 타입' 과 같이 기술한다.
예를 들어 menuList[0..10] : Menu 라고 하면 0부터 10 의 인덱스를 가진, 11개의 메뉴 리스트를 가리킨다.
2. 메서드 시그니처 추가 (매개변수 이름 및 타입, 반환 타입 추가)
클래스 다이어그램에 메서드(operation)를 기술할 때는 다음과 같이 기술한다.
이름 (매개변수 리스트) : 반환타입
이때 메서드의 '시그니처' 는 다음 3가지로 결정된다.
1. 메서드 이름
2. 파라미터의 타입과 개수
3. 반환 타입
메서드의 반환 타입은 void 타입과 같이 없는 경우 생략할 수 있으며,
기본 생성자, 소멸자, getter, setter 와 같은 메서드의 경우 클래스 다이어그램에 굳이 표기하지 않아도 괜찮다.
하지만 기본 생성자가 아닌 생성자의 경우에는 표기해주어야 한다.
3. visibility 기술
+ : public
- : private
# : protected
~ : package
패키지의 경우, 같은 패키지 안에 있는 클래스라면 접근할 수 있다는 것을 나타낸다.
이때 '패키지' 는 클래스의 이름이 겹칠 수 없는 영역을 나타낸다. (다르게 말하면 다른 패키지에 있다면 클래스 이름이 겹칠 수 있다.)
규모가 큰 프로젝트의 경우, 서로 다른 패키지에서 개발할텐데, 이름을 겹치지 않게 짓도록 강제하면 개발시 불편하므로 패키지 안에서만 겹치지 않게 하려고 등장한 개념이다.
4. 메서드에 책임 할당하기
(2번 과정을 하고나면 자연스럽게 달성)
5. UI 를 다룰 클래스 추가
(다음 글에서 더 자세히 다룬다.)
Coupling & Cohesion
소프트웨어의 설계가 좋은지 나쁜지 판단하는 기준으로 coupling 과 cohesion 이 있다.
오브젝트라는 책에서는 '결합도' 와 '응집도' 라고 번역하였다.
결합도는 두 컴포넌트가 서로 상호작용하는 정도를 나타낸 것으로, 결합도는 낮을수록 좋다.
응집도는 한 컴포넌트가 하나의 목표를 달성하는 정도를 나타낸 것으로, 응집도는 높을수록 좋다.
이 두 개념은 상호 배타적이지 않고, 서로가 서로를 보조하는 개념이다.
결합도를 측정하는 방법은 두 가지가 있다.
1. 한 오브젝트가 다른 오브젝트에게 보내는 메세지 타입의 가짓수 세기
2. 이 메세지들의 파라미터 개수 세기
따라서 한 오브젝트에서 다른 오브젝트로 보내는 메세지 (호출하는 메서드) 의 개수가 적고,
하나의 메서드에서 전달하는 매개변수의 가짓수가 적을수록
두 오브젝트 사이의 결합도가 낮다고 판단한다.
결합도가 낮으면 구현이 변할 가능성이 줄어들고 재사용이 더 쉬워진다.
한 오브젝트에서 이 오브젝트, 저 오브젝트 여기저기에 메세지를 보내고 있다면,
다른 오브젝트의 메서드가 변했을 때 이 오브젝트 역시 수정되어야 하며,
파라미터로 이 오브젝트, 저 오브젝트를 넘기고 있다면 다른 오브젝트가 수정되었을 때,
그 파라미터가 정의된 메서드 역시 변해야 한다.
즉, 다른 오브젝트와 결합도가 높다는 것은 변화에 취약해진다는 것과 같다.
응집도는 크게 메서드(operation) 응집도와 클래스 응집도로 구분한다.
메서드 응집도는 하나의 메서드가 하나의 기능에 집중하고 있는지를 나타낸다.
예를 들어 Shape 클래스 안에 calculateAreaSize() 메서드가 정의되어있고, 그 구현이 Shape 클래스의 width * height 를 반환한 것이라고 해보자.
이 메서드는 클래스가 가진 값을 읽어 '넓이를 계산한다' 라는 한 가지 기능에만 집중하고 있다.
따라서 이 메서드의 응집도는 높다.
클래스 응집도는 클래스가 하나의 필요사항에 집중하고 있는지를 나타낸다.
예를 들어 Member 클래스 안에 이름, 나이 정보와 함께 '도로명주소', '상세주소', '우편번호' 정보가 함께 저장되어 있다고 해보자.
이때 '도로명주소', '상세주소', '우편번호' 정보는 Address 라는 별도 클래스로 묶어낼 수 있다.
따라서 이 Member 클래스의 응집도는 낮다.
Inheritance Degree
설계의 좋고 나쁨을 판단하는 척도로 inheritance degree 도 있다.
이 속성은 서브 클래스가 상위 클래스의 모든 기능을 다 필요로 하고 있는지를 나타낸다.
예를 들어 '전자기기' 클래스에 '전원 켜짐 여부', '볼륨', '충전중 여부' 속성과 '전화걸기', '메세지보내기' 메서드가 있다고 해보자.
그리고 이 클래스를 상속하는 '냉장고' 클래스가 있다고 해보자.
냉장고 클래스는 '전원 켜짐 여부', '볼륨' 속성은 필요로 할 지 몰라도
배터리가 없기 때문에 '충전중 여부' 속성은 필요가 없으며 '전화걸기', '메세지 보내기' 메서드도 필요없다.
이처럼 부모 클래스로부터 필요없는 속성과 메서드를 상속받고 있다면, 이 설계는 나쁘다.
(poor inheritance degree)
위에서 말했던 Member 클래스로부터 주소 정보를 분리하여 Address 클래스를 만들었다고 해보자.
이때 Member 클래스가 Address 정보를 가져야 한다고 해서 Member 가 Address 클래스를 '상속' 받도록 설계하면 이는 잘못된 설계이다.
Member is a kind of Address 가 아니기 때문이다.
이 둘은 Has-A 관계가 더 적절하므로 1:1 관계 (multiplicity) 를 갖는 association 을 Member -> Address 방향으로 걸어주는 것이 더 적절하다.
Association
one-to-one
두 클래스 사이에 association 이 존재한다는 것은 두 클래스의 오브젝트 사이에 link 가 존재할 가능성이 있음을 나타낸다.
이때 다른 오브젝트에게 메세지를 전달하려면 그 오브젝트에 대한 참조를 가지고 있어야만 한다.
또한 association 을 표시할 때는 메세지의 전달 방향도 화살표로 함께 나타낸다.
그림으로는 위와 같이 나타낼 수 있다.
Member 와 Address 사이에는 has-a 관계가 존재한다.
이때 Address 를 가지는 객체는 Member 이므로 Member 에서 Address 방향의 화살표를 그려준다.
그리고 Member 클래스에 Address 타입의 멤버 변수를 attribute 로 추가해준다.
이렇게 한쪽 방향으로 association 이 존재하는 것을 가리켜 one-way association 이라고 말한다.
(정확히는 위 사례는 one way one-to-one associatioin 이다)
만약 두 객체가 서로 메세지를 주고 받는다면, 서로에게 메세지를 보내기 위해 둘 다 서로의 참조를 attribute로 갖고 있어야 한다.
그림으로는 이렇게 나타낼 수 있다.
Address 도 Member 에게 메세지를 전달해야 한다면 Address 에서 Member 가는 방향의 화살표가 추가되고
Address 의 attribute 에 Member 타입의 멤버 변수가 추가되어야 한다.
이를 가리켜 two-way association 이라고 한다.
two way association 은 사실 그렇게 좋은 설계는 아니다.
두 객체 사이의 주고받는 메세지가 늘어나므로 coupling 이 높아지기 때문이다.
one-to-many
one-to-one 에서 했던 것처럼 one-to-many 를 표현하면 이렇게 표현할 수 있다.
레스토랑이 메뉴를 가지고 있고, 레스토랑에서 메뉴로 메세지를 전달하므로 화살표는 레스토랑에서 메뉴 쪽으로 뻗는다.
레스토랑은 메뉴를 여러개 가지고 있으므로, 메뉴들의 참조를 리스트로서 레스토랑이 attribute 로 보유한다.
물론 이렇게 설계해도 프로그램의 동작에는 문제가 없다.
하지만 이 설계는 아쉬운 점이 있다.
레스토랑이 메뉴 리스트를 직접 보유하게 되면, 이 리스트에 새로운 메뉴를 추가하고 삭제하는 메서드도 레스토랑이 가져야 한다.
이렇게 되면 레스토랑은 '레스토랑' 이 해야 할 기능과 더불어 '메뉴 리스트를 관리' 하는 기능까지 함께 맡게 된다.
즉, 응집도(cohesion)가 낮아진다.
따라서 응집도를 높이기 위해 '메뉴 리스트를 관리' 하는 기능은
메뉴 객체에 대한 collection 클래스로 분리하는 방법을 생각할 수 있다.
그러면 위와 같이 컬렉션 클래스를 1:1 관계로 갖는 형태로 나눠서 생각할 수 있다.
이렇게 설계하면 더 응집도가 높은 방향으로 설계할 수 있게 된다.
컬렉션 클래스를 활용해서 메뉴 리스트 이름을 조회하는 listMenu() 메서드의 로직을 표현하면 위 그림과 같이 그릴 수 있다.
menuList 의 크기가 0이라면 opt operator 정의에 따라 이 combined fragment 영역 전체가 실행되지 않는다.
처음에는 컬렉션 클래스의 findFirst() 메서드를 호출하여 첫 번째 메뉴의 이름을 리스트에 저장하고
다음부터는 컬렉션 클래스의 getNext() 메서드를 호출하여 이후 메뉴의 이름을 리스트에 저장한다.
many-to-many
마지막으로 many-to-many 관계를 표현하는 방법을 살펴보자.
선생님이 여러 반을 담당할 수 있고, 한 반에도 여러 담당 선생님이 있다고 해보자.
그러면 단순하게 이렇게 표현할 수 있다.
선생님이 담당하는 클래스 리스트를 멤버 변수로 갖고, 클래스에도 담당 선생님 리스트를 멤버 변수로 갖는 것이다.
하지만 역시 각 리스트의 데이터를 관리하는 기능을 teacher, classroom 모두가 가져야 한다는 점에서는 응집도가 낮다.
따라서 두 컬렉션 클래스를 분리하여 응집도를 높여보자.
그러면 위 그림과 같이 그릴 수 있다.
(two-way many-to-many association)
'CS > 소프트웨어공학' 카테고리의 다른 글
[소프트웨어공학] 14. Object Interaction (0) | 2025.06.04 |
---|---|
[소프트웨어공학] 13. Requirement Analysis (4) | 2025.06.03 |
[소프트웨어공학] 12. Configuration and Version Management (0) | 2025.04.21 |
[소프트웨어공학] 11. Use Case Diagram (1) | 2025.04.21 |
[소프트웨어공학] 10. Requirement Capture (0) | 2025.04.20 |