UML(Unified Modeling Language)은 모델링 언어로 소프트웨어가 어떻게 구조화되었고, 객체들 끼리 어떻게 상호작용하는 지, 그리고 객체들 끼리의 관계가
어떻게 되는 지를 표현하는데 도움을 준다. UML은 이러한 소프트웨어 구조와 객체들의 관계를 하나의 다이어그램으로 표현하여 시각화해주고, 이해를 도와준다.
여기서 다이어그램은 두 가지로 구분할 수 있다.
클래스 다이어그램
만을 다루도록 하자.위의 class diagram
이미지를 보면 알 수 있듯이, 클래스의 구조와 이들 간의 관계가 아주 잘 묘사되어있다.
따라서, class diagram
을 통해 어플리케이션의 클래스가 어떻게 구조화되었고, 관계가 어떤 지를 확인하기 좋다.
그러나, class diagram
만으로는 부족한 점이 있는데, 어떤 관계인지는 알 수 있어도, 어떻게 상호작용할 지는 알기쉽지 않다.
interaction diagram
은 behavior diagram
의 한 부류로behavior diagram
에는 sequence diagram
이 OOP에 널리 사용된다.이럴 때에는 behavior, interaction
이 중요하기 때문에 객체 또는 클래스들이 어떻게 상호작용하는 지 나타내는
sequence diagram
을 사용하는 것이 좋다.
OOP에서 상속 관계말고도 복잡한 소프트웨어 시스템을 모델링하는데 도움을 주는 다른 클래스 관계
들이 있다.
상속
은 is-a
관계로 불린다. 왜냐하면 어떤 클래스로부터 상속을 받은 자식 클래스는 부모 클래스의 superclass
로 사용될 수 있기 때문이다.
즉, 상속받은 자식 클래스는 부모 클래스의 속성과 행동을 가져오고 확장을 할 수 있기 때문에 is-a
관계가 성립가능 한 것이다.
가령, 벤츠는 탈 것이다.
에서 벤츠
는 자식 클래스가 되고, 탈 것
은 부모 클래스가 된다. 탈 것
의 속성과 행동을 벤츠
가 상속받고, 확장하여
벤츠
만의 속성과 행동을 확장할 수 있다. 따라서, 이들은 is-a
라고 표현할 수 있는 것이다.
그런데, 이런 탈 것
이라는 클래스가 사실 실제로 존재하는 것은 아니다. 탈 것
은 이름 뿐인 존재이다. 즉, 개념적인 존재라는 것이다.
반면 벤츠, 현대차, 아우디, 포르쉐 등등
은 실제로 존재하는 자동차들이다. 우리는 탈 것
이라는 이름만 존재하는 개념을 먼저 생각하여 각각의
자식인 벤츠, 현대차, 아우디, 포르쉐 등
에 속성과 행동을 부여할 수 있지만, 실제 세계에서는 이렇게 처음부터 생각하기가 쉽지 않다.
그래서, 반대로 올라가는 개념을 차용하는데, 그것이 바로 Generaliza (일반화)
이다. 2개 이상의 클래스들이 공통적인 특성을 갖고 있다면
이를 상위 부모 클래스의 속성으로 묶에 자식 클래스에 속성을 부여할 수 있다. 이것이 Generalize( 일반화 )
라는 것이다.
가령, 벤츠, 현대차, 아우디, 포르쉐 등
자동차들 모두 drive, logo, handle
등의 속성과 행동을 갖고 있다. 이를 Vehicle(탈 것)
이라는 부모 속성으로
Generalize
하여 상속해주는 것이다. 반면, 부모 클래스로부터 특별한 속성을 갖는 자식 클래스를 만드는 경우가 있는데, 이를 specialization
이라고 한다.
즉, Generalize
는 아래에서 위로 올라가는 구조화 방식이라면, Specialization
은 위에서 아래로 구조화하는 방식인 것이다. 가령, 먼저 Vehicle
을 생각하고
벤츠, 현대차, 아우디, 포르쉐 등
각 자동차 회사의 특성에 맞는 클래스를 구체화하는 것이다.
Globalization
이 UML에서 OOP의 상속
에 관한 것이라면, Realization
은 UML에서 OOP의 interface
의 클래스 구현을 말한다.
만약, <<Lockable>>
이라는 인터페이스가 있다고하자, 이는 문을 잠글 수 있다는 것인데, 자동차도 문을 잠글 수 있으므로 이를 implementation(구현)
해야한다.
따라서, 다음의 관계를 갖는다.
즉, Realization
은 아무것도 구현되지 않은 인터페이스를 받아 구현체 클래스가 실체화, 실현하는 관계도를 표현한다.
의존성은 UML과 관계에서 가장 일반적인 타입이다. 한 클래스가 어떤 식으로든 다른 클래스에 의존하는 반면, 다른 클래스는 의존할 수도 있고, 그렇지 않을 수도 있음을 정의하는데
사용되낟. 때문에 종속관계는 Uses-A관계라고도 한다.
dependency
는 별달리 특별한게 아니고, A 클래스의 메서드에서 파라미터로 B 클래스의 인스턴스를 사용한다면, 'A는
B에 의존한다.' 라고 할 수 있다. 또한, A클래스가 B클래스의 인스턴스를 지역변수로 갖고있던지, 또는 A클래스가 B클래스의 인스턴스를
생성하는 것 역시도 의존 관계
에 있다고 볼 수 있다.
즉, A클래스가 B클래스를 참조하고 있는 것만으로도 의존 관계가 성립된다는 것이다.
public class User{
public Food makeFood(){
return new Food();
}
public void getFood(Food food){
Food foodInstance = food;
}
}
다음과 같이 User
클래스가 Food
클래스를 생성, 파라미터로 입력 받으면 User
은 Food
클래스에 Dependency
가 있다.
라고 할 수 있다. 즉, User --> Food
가 되는 것이다. 의존하는 쪽이 화살표를 던지는 것이다. 마치 짝사랑마냥
근데, 왜 의존
이라고 표현하는가? 라고 생각할 수 있는데, 이는 User
가 Food
객체의 상태에 따라 다르게 로직을 처리할 수 있기 때문이다.
반면, Food
는 User
가 자신에게 의존하는 지를 모른다. Food
는 사실 내부적으로 User
에게 아무런 영향을 받지 않는다. 즉, User
의
Food
짝사랑인 것이다. 따라서, 의존
관계는 한 쪽은 영향을 받지만, 한 쪽은 영향을 전혀 받지 않은 것을 말하는 관계이기도 하다.
Association
관계에는 두 가지가 있다. composition(포함)
관계와 aggregation(집합)
관계이다.
Association
은 별 것이 없는데, 그냥 코드 상으로보면 클래스의 맴버변수에 해당 클래스가 있는 것이다.
public class User{
public Food food;
}
다음과 같이 생긴 것을 말한다.
association
은 다음과 같이, User
가 Food
을 연관하고 있다. 라고 생각하면 된다. 위에도 말했듯이 화살표 쏘는 방향이 짝사랑이다.
즉, User
은 Food
를 지지고 볶고하지만, Food
의 내부는 User
과 별 상관없다.
Aggregation
은 Association
의 특별한 타입으로, 상속의 is-a관계와는 달리 has-a관계를 갖는다.
Aggregation
은 한 클래스가 다른 클래스를 논리적인 관점으로 포함할 때 사용하는데, 두 가지 조건 중 하나를 만족해야한다.
whole
이 part
여러 개를 포함하고 있다고 하자.whole
이 죽어도(사라져도) part
는 남아있는다.whole
말고도 part
를 사용하고 있는 클래스가 있어, part
는 공유로 쓰인다.즉, 정리하면 whole
과는 독립적으로 part
가 존재하고 있어야 한다는 것이다. whole
이 사라져도 part
는 그대로 있거나,
whole
만이 아니라 다른 클래스도 part
를 가질 수 있도록 공유한다는 것이다.
이는, 학과
와 선생
의 관계가 될 수 있다. 학과
는 선생
을 소유한다.(association
) 또한, 한 학과
는 여러 선생
을 가질 수 있다.
그러나, 학과
가 사라져도 선생
은 사라지지 않는다. 또한, 선생
은 학과
말고도 다른 곳에 속할 수 있다.
따라서, 이들은 Aggregation
관계이 있다고 한다.
그림은 좀 난해할 수 있는데, 다이아몬드
모양은 화살표가 아니다. 즉, 짝사랑의 방향이 아니다. 사실은 아래처럼 화살표 모양을 그릴 수도 있다.
그러나, 다이아몬드 모양이 있으면 반대편이 화살표 모양이라는 것을 내포할 수 있기 때문에, 화살표를 생략하기도 한다. 그럼, 다이아몬드 모양은 무엇을 의미한 것일까??
다이아몬드 모양은 소유하고
있는 인스턴스의 개수가 꽤 많다는 것을 의미한다. 또한, 비었다는 것은 이들이 강하게 연결되어 있는 건 아니라는 것을 말한다.
즉, Department
가 죽어도 Teacher
까진 죽는게 아니고, 독점적으로 Department
가 Teacher
를 소유한 것도 아님을 알려준다.
Composition
관계는 말 그대로, 다른 클래스에 어떤 클래스가 포함된다는 것을 말한다. 이 역시도 has-a
관계를 가지는 것이다.
그럼, Aggregation
과 무엇이 다를까? 이는 짝사랑하는(main 클래스)가 죽으면 반대편도 죽는다는 것이다.
가령, 집
을 생각해보자, 집
은 공간
을 갖는다. 만약, 집
이 사라지면 공간
도 사라지게 된다. 이는 aggregation
관계가 아니라,
Composition
관계라고 한다. 그림은 다음과 같이 표현한다.
다음과 같이 생긴 모습이다. 다이아몬드 모양은 여러 개 갖고 있음을 말한다. 그러나, aggregation
과는 달리, 다른 클래스들과
Room
을 공유할 수도 없고, House
가 죽으면 Room
도 죽는다. 따라서, 강한 의존관계를 갖고 있기 때문에 검은색으로 칠한 것이다.
사실, 코딩의 관점에서 이건
composition
이야, 이건aggregation
이야를 구분할 필요는 없다. 실제JAVA
같은 언어에서
원하는 타이밍에 객체를 삭제하는 것이 어렵기 때문이다. 보통 가비지 컬렉터가 이 일을 대신하고 사용자는 손놓고 있는다. 따라서, 코딩 단계에서는 굳이
이를 확실히 구분하려고 하진 않지만, 정밀한 UML 단계에서는 구분하려고 한다.
위의 예제를 보면 UML에 숫자가 있는 것을 확인할 수 있다. 아래의 aggregation
예제를 보자
다음은 one-to-many
관계이다. 이는, 한족 클래스는 상대방 클래스의 0~N개의 인스턴스를 가질 수 있고, 반대는 상대방 클래스의 0~1개의 인스턴스를
가질 수 있다는 것을 말한다.
단, 자신이 가질 수 있는 상대방 클래스의 인스턴스 개수는 자신의 앞이 아니라, 상대방에 표시한다. 따라서, department
클래스는 Teacher
클래스의 인스턴스를 0~n개 가질 수 있는 것이고,
Teacher
클래스는 Department
인스턴스를 0~1개만 가질 수 있는 것이다.
이 숫자를 Multiplicity 라고 부른다.
Multiplicity = Cardinality + Participation 으로 구성되어있다.
Cardinality
는 상대방 클래스의 인스턴스를 가질 수 있는 최대 수를 말한다. 즉, 0~N 이라고 하면 N이 된다. 또는 0~1이라고 하면 1이 된다.
반면Participation
는 상대방 클래스 인스턴스를 가질 수 있는 수 중에 가장 작은 값을 말한다. 즉, 최소 값이다. 0~N이라고 하면 0이고, 1~N이라고 하면 1이다. 이 둘을 합쳐 부르는 것이
Multiplicity
이며 0~N, 0~1, 1~N 등 최대, 최소를 모두 표현한 것을Multiplicity
라고 부른다.
one-to-one
관계도 있다.
다음은 커플의 경우 보통, 남녀 한 쌍이기 때문에 남자는 여자를 인스턴스를 하나만 포함하고, 여자도 남자 인스턴스를 하나만 포함한다.
물론, 이렇게 말하고 나면 수많은 예외를 생각하면서 아닌데? 아닌데? 라며 반례를 들 수 있다. 그러나, 이런 관계도가 꼭 세상의 모든 이치에 상응해야하는 것은 아니다.
어느정도의 compensate는 있어야 한다. 가령, 정밀함을 버리고 단순함을 취한다는 것이 있다.