5 분 소요

2. 클래스 간의 관계

연관 관계

양방향 연관 관계

  • 단순한 연관 관계로 ‘교수는 학생에게 수업하고, 학생은 교수에게 수업을 받는다’로 해석

역할이 부여된 연관 관계

  • 연관 관계에서 각자에게 역할을 부여할 수 있음
  • 교수의 역할은 ‘강의자’라 할 수 있고 학생의 역할은 ‘수강자’

sw_6_1_1

다중 연관 관계

  • 다중 관계는 다양하게 나올 수 있으며 선 위에 다중성을 표기해 나타냄
  • 일 대 다(1 : n) 관계 의 예는 교수 1명이 여러 명의 학생을 가르치는 관계라 할 수 있음
  • 4명의 교수가 다수의 학생을 가르칠 경우도 있음
  • 다 대 다 관계 는 실제로 구현하기가 어려우므로 일 대 다 관계를 변환해 사용하는 것이 좋음

sw_6_1_2

단방향 연관 관계

  • 두 클래스가 서로 아는 관계가 아니고 한쪽만 아는 관계를 나타냄
  • 두 클래스 간의 연결선이 방향성을 나타내는 화살표가 있는 직선 실선

연관 클래스

  • 연관 관계를 더 구체적으로 나타내고 싶을 때 클래스를 추가해 사용하는 것
  • 점선을 사용해 나타내며, 일반 클래스처럼 다른 클래스와도 연관 관계를 맺을 수 있다.

sw_6_1_3

일반화 관계

일반화와 상속

  • ‘일반적’의 사전적 의미: 일부에 한정되지 아니하고 전체에 걸치는 것 , 보편적이고 상식적인 것
  • 클래스에서 일반화: 공통점을 가지고 있는 여러 개의 클래스를 묶어서 새로운 클래스를 만들고 공통적인 이름을 붙인 것
  • 일반화 관계는 상속 구조이며 하위 클래스는 상위 클래스의 모든 것(속성, 메서드)을 상속받아 사용
  • 아래에서 위로 추상화되는 것을 일반화, 반대로 위에서 아래로 구체화하는 것을 특수화라고 함
  • 개별 클래스와 공통 클래스 사이에 ‘is a kind of’ 관계가 성립되어야 함
  • is a kind of 관계 : ‘사각형은 도형의 일종이다’, ‘원은 도형의 일종이다’

sw_6_1_4

집합 관계

집합 관계

  • ‘상위 클래스가 하위 클래스로 구성될 때의 관계로 ‘is composed of’가 성립되어야 함
    • is composed of 관계 : ‘컴퓨터는 모니터, 본체, 키보드로 이루어졌다’라고 할 때 말이 되는 관계
  • 모든 객체가 별개의 생명주기를 가지고 있으며 각각 독립적으로 동작하기 때문에 약한 결합 관계
  • 집합 관계에서 전체(컴퓨터)와 부분(모니터, 본체, 키보드)의 연결은 직선으로 하되 머리 부분은 속이 비어 있는 마름모 모양

sw_6_1_5

합성 관계

합성 관계

  • 집합 관계와 많은 부분이 유사하지만 전체 객체에 완전히 종속되어 독립된 객체로 존재할 수 없다는 것이 다름
  • 모든 객체가 같은 생명주기를 가지고 있으므로 각각 독립적으로 동작할 수 없는 강한 결합 관계
  • 전체(노트북)와 부분(모니터, 본체, 키보드)의 연결은 직선으로 하되 머리 부분은 속이 채워진 마름모 모양

sw_6_1_6

의존 관계

연관 관계와 의존 관계의 차이점

  • 서로 상대의 클래스를 사용(참조)할 때의 관계로 클래스 A의 변화는 클래스 B의 변화로 연결되는 점에서 연관 관계와 유사
  • 연관 관계는 상대 클래스를 인식하기 위해 멤버 변수를 가짐
  • 클래스 A인 Repeater 클래스가 클래스 B인 Scan 클래스를 멤버 변수로 가지고 있음

sw_6_1_7

연관 관계와 의존 관계의 차이점

  • 의존 관계는 상대의 메서드를 가지고 있음
  • 예시) 클래스 A의 메서드가 클래스 B의 객체를 인자로 받아 그 메서드를 사용할 경우
  • 색 글자로 표시된 부분을 보면 Scan 클래스를 인자(파라미터)로 받아 사용

sw_6_1_8

연관 관계와 의존 관계의 차이점

  • 의존 관계는 상대의 메서드를 가지고 있음
  • 예시) 클래스 A인 Repeater 클래스의 메서드인 repeat() 내부에서 클래스 B의 객체를 생성해 메서드를 사용할 경우
  • 클래스 A인 Repeater 클래스는 클래스 B인 Scan 클래스를 지역 변수로 사용

실체화 관계

  • 특정 클래스에 기능을 추가하기 위해 메서드를 넣을때 하위 클래스가 상속을 무조건 받아야 하는 사태가 발생
  • 인터페이스 클래스는 메서드의 공통 특성을 묶어 새로운 인터페이스 클래스를 만들고 공통의 이름을 붙임
  • 이 클래스는 변수를 정의할 수 없고 추상 메서드를 가지며 이 메서드에 대한 구체적인 실현은 하위 클래스에서 구현
  • 스테레오타입으로 «»를 사용하고 하위 클래스와의 연관은 일반화 관계와 다르게 점선으로 나타냄
  • 화살표의 머리 부분은 하위 클래스에서 상위 클래스로 향하며 모양은 속이 빈 삼각형

sw_6_1_9

3. 클래스 설계 원칙

단일 책임 원칙 (SRP)

단일 책임 원칙: 클래스를 변경해야 하는 이유는 단 하나여야 한다.

단일 책임 원칙

  • 로버트 마틴: 책임의 의미는 변경하는 이유
  • 단일 책임의 원칙은 클래스를 설계할 때 변경하는 이유가 하나가 되도록 해야 한다는 것
  • 모든 클래스는 한 가지 책임, 즉 변경의 이유를 오직 하나만 갖도록 설계하고 클래스는 그 책임을 완전히 캡슐화하는 것
  • 클래스에 2개의 책임이 존재한다면 2개의 클래스로 분리해 ‘변경해야 하는 이유가 오직 하나 가 되도록’ 설계하는 것이 바람직

sw_6_1_10

개방 폐쇄 원칙 (OCP)

개방 폐쇄 원칙: 변경에는 닫혀 있어야 하고, 확장에는 열려 있어야 한다.

개방 폐쇄 원칙

어떤 것은 개방하고 어떤 것은 폐쇄하는 것

Video_Palyer 클래스의 예시: 원칙 위반

  • 처음에는 MP4만 지원하던 Video_Palyer 클래스에 지원가능한 파일 형식이 늘어날때마다 클래스를 수정
  • 개방 폐쇄 원칙을 따르지 않으면 지원 가능한 파일 형식이 늘어날 때마다 계속 클래스를 수정해야 함

sw_6_1_11

Video_Palyer 클래스의 예시: 원칙 적용

  • 계속해서 바뀌는 것이 무엇인지를 찾고 그것들을 모아 상속 구조로 만들어 상위 클래스에 배치
  • 하위 클래스에는 코덱의 종류를 배치해 계속 추가할 수 있도록 함
  • Video_Player 클래스에서 이들을 호출해 사용하도록 설계

sw_6_1_12

개방 폐쇄 원칙의 장점

  • 변경에 닫혀 있는 설계: 계속된 추가 사항이 있어도 그들끼리만 추가할 수 있도록 설계
  • 확장에 열려 있는 설계: 새로운 클래스를 쉽게 추가할 수 있는 구조로 설계

리스코프 교체 원칙 (LSP)

리스코프 교체 원칙: 상위 클래스의 객체는 언제나 자신의 하위 클래스의 객체로 교체할 수 있어야 한다.

리스코프 교체 원칙

  • 상위 클래스의 객체가 들어갈 자리에 하위 클래스의 객체를 넣어도 문제없이 잘 작동해야 함
  • 리스코프 교체 원칙은 상속과 재정의의 중요성을 강조함
  • LSP를 지키는 클래스 설계를 위해서는 상속 구조를 만들 때 상위 클래스 추상 클래스와 추상 메서드여야 함
  • 재정의를 사용할 때는 피터 코드의 상속 규칙에 맞게 사용해야 함
  • 피터 코드의 상속 규칙 : ‘하위 클래스는 상위 클래스의 책임을 무시하거나 재정의하지 않고 확장만 수행한다’

리스코프 교체 원칙: 대학의 강사료 계산 프로그램의 예시

sw_6_1_13

  • 이 클래스 다이어그램은 Lecturer()를 하위 클래스에서 재정의해 사용함
  • 상위 클래스의 Lecturer()를 대학원 시간 강사의 Lecturer()로 대체하면 문제가 발생
    • 일반 강사의 강사료는 90,000, 대학원 시간 강사의 강사료는 180,000원으로 대체 시 일반 강사가 180,000를 받게됨
    • 재정의를 무분별하게 사용했기 때문에 오류가 발생

sw_6_1_14

  • 재정의는 상이 클래스의 메서드가 추상 메서드일 경우에만 사용

sw_6_1_15

의존 관계 역전 원칙 (DIP)

의존 관계 역천 원칙: 클라이언트는 구체 클래스가 아닌 추상 클래스(인터페이스)에 의존한다.

의존 관계 역전 원칙

  • 객체지향 설계에서 서로의 관계가 의존적이라는 것은 결합도가 높다는 것이고 이는 클래스 설계에서 좋은 관계가 아님
  • 의존 관계 역전 원칙에서는 구체 클래스에 의존하도록 클래스 설계를 하지 않고 자주 바뀌는(추가/삭제) 클래스를 상속 구조로 만들어 추상 클래스에 의존하도록 설계
  • ‘고수준 구성 요소가 저수준 구성 요소에 의 존하면 안된다’는 의미

GameServer 클래스의 예시: 원칙 위반

  • 조건문을 추가하여 조건의 변화(추가/삭제)에 따라 수정할 수밖에 없다는 것을 의미하므로O CP(개방 폐쇄 원칙) 위반
  • 구체 클래스인 GameServer에서 직접 생성하면 GameServer 클래스는 생성된 모든 게임 클래스에 직접 의존
  • 각 게임 클래스가 업데이트 될 경우 GameServer 클래스는 객체선언부터 조건문까지 모두 변경해야함

GameServer 클래스의 예시: 원칙 적용

  • 게임을 쉽게 추가/삭제할 수 있도록 상속 구조로 만듬
  • GameServer 클래스가 추상 클래스인 Games를 참조하도록 만듬
  • 클래스 다이어그램을 보면 GameServer가 Games라는 추상 클래스에 의존
  • 모든 게임 클래스도 추상 클래스인 Games에 의존

sw_6_1_16

sw_6_1_17

sw_6_1_18

인터페이스 분리 원칙 (ISP)

인터페이스 분리 원칙: 클라이언트는 자신이 사용하지 않는 메서드와 의존 관계를 맺으면 안된다.

인터페이스 분리 원칙

  • 다수의 클라이언트가 일반적인 인터페이스 하나를 같이 사용하는 것보다 각각의 클라이언트가 필요한 대로
  • 여러 개의 구체적인 인터페이스로 분리해 사용하는 것이 더 낫다는 의미
  • 무조건 모든 클라이언트에게 각각의 인터페이스를 제공하는 의미가 아님
  • 각각의 클라이언트가 필요로 하는 메서드군이 존재할 때 인터페이스를 분리

인터페이스 분리 원칙의 장점

  • 용도가 명확한 인터페이스를 제공할 수 있어 클래스에 필요한 메서드만 선언할 수 있음
  • 시스템의 내부 의존성을 낮춰(낮은 결합) 리팩토링을 쉽게 해 재사용성을 높일 수 있음

학사관리 클래스의 예시: 원칙 위반

  • 학사관리 클래스에 많은 메서드가 있고 이 메서드를 학생, 교수, 직원이 사용
  • 학생이 사용하는 것은 수강신청()과 성적조회()뿐인데 사용하지 않는 다른 메서드와도 연관 관계를 맺고 있음
  • 직원과 교수도 마찬가지로 사용하지 않는 메서드와 연관 관계를 맺고 있음

sw_6_1_19

학사관리 클래스의 예시: 원칙 적용

  • 클래스 다이어그램에서는 사용자(학생, 직원, 교수)별로 인터페이스를 분리
  • 각 사용자는 오직 자신이 필요로 하는 메서드와 연관되어 있고 다른 메서드와는 관계가 없음
  • 학생 인터페이스의 수강신청(), 성적조회()의 구현은 학사관리 클래스에 그대로 남아 있음
  • 단일 책임 원칙 과 혼동해서는 안됨

sw_6_1_20

댓글남기기