[SOLID] 01. 객체지향 모델링


객체지향 모델링(Object-Oriented Modeling, OOM)은 시스템을 객체라는 기본 단위로 나누고, 객체 간의 상호작용을 정의하여 문제를 분석하고 설계하는 방법이다. 모델링(Modeling)에 대한 설명과 UML과 UML다이어그램 중에 클래스 다이어그램 중심인 포스팅이다.


1. 모델링

컴퓨터 공학 및 소프트웨어 개발에서 모델링(Modeling)은 소프트웨어 시스템, 프로세스, 데이터 구조 등을 설계하고 표현하기 위해 구조화된 형태의 추상화를 생성하는 작업이다. 이는 복잡한 소프트웨어를 명확히 이해하고 효과적으로 개발하며, 문제를 체계적으로 해결하기 위한 중요한 단계이다.

1-1. 모델링의 목적

1) 시각적 표현

소프트웨어 시스템을 다이어그램이나 도식으로 표현하여 복잡한 시스템을 명확히 이해

2) 효율적 설계

요구사항 분석을 바탕으로 구조를 미리 설계하여 개발 과정에서 발생할 수 있는 오류를 줄임

3) 의사소통

개발자, 디자이너, 비즈니스 이해관계자 간의 의사소통을 돕는 언어로 활용

4) 문제 해결

문제를 체계적으로 정의하고 솔루션을 설계하기 위해 활용

5) 재사용 및 확장 가능성

설계 단계에서 재사용 가능한 구조를 고려해 미래 확장을 용이하게 만듬



2. UML

시스템을 모델로 표현해주는 대표적인 모델링 언어로 Unified Modeling Language (UML)이 있다. UML은 요구분석, 설계, 구현 등 개발 과정에서 개발자 사이의 의사 소통이 원활하게 이루어지도록 표준화한 통합 모델링 언어이다. UML은 객체지향 소프트웨어 개발에서 널리 사용되며, 시스템의 구조와 동작을 명확히 설계, 시각화, 문서화하기 위한 도구로 활용된다.


2-1. UML의 주요 특징

  1. 표준화: OMG(Object Management Group)에서 정의한 표준 모델링 언어
  2. 다양성: 시스템의 구조적, 동적 측면을 모두 표현 가능
  3. 도구 독립성: UML을 지원하는 다양한 소프트웨어 설계 도구에서 활용 가능
  4. 유연성: 소프트웨어 외에도 비즈니스 프로세스, 데이터베이스 설계 등 다양한 분야에 적용 가능


2-2. UML 다이어그램의 종류

UML2.0에선는 시스템의 `구조(Structure)와 동작(Behavior)을 표현해서 다이어그램을 제공하고 있다. UML은 크게 구조적 다이어그램행동적 다이어그램으로 나뉜다.

1) 구조적 다이어그램(Structure diagram)

시스템의 정적 측면을 표현하며, 주로 구성 요소와 관계를 나타낸다.

1.1) 클래스 다이어그램 (Class Diagram)

  • 객체지향 시스템에서 클래스와 그들 간의 관계를 표현
  • 클래스의 속성, 메서드, 상속, 종속 등을 정의

1.2) 객체 다이어그램 (Object Diagram)

  • 클래스의 인스턴스(객체)를 구체적인 정보를 보여줌

1.3) 컴포넌트 다이어그램 (Component Diagram)

  • 시스템의 컴포넌트(물리적 구성 요소(모듈, 라이브러리 등))간 관계를 나타냄

1.4) 배치 다이어그램 (Deployment Diagram)

  • 소프트웨어의 실행 시스템의 물리(하드웨어 노드, 네트워크 등) 구조를 표현

1.5) 패키지 다이어그램 (Package Diagram)

  • 클래스, 컴포넌트 등을 그룹화한 패키지로 구성하고 패키지들간의 관계를 표현


2) 행동적 다이어그램(Behavior diagram)

시스템의 동적인 측면과 동작을 표현하며, 시간적 흐름이나 상호작용을 나타낸다.

2-1) 활동 다이어그램 (Activity Diagram)

  • 프로세스의 흐름(작업, 조건, 병렬 작업, 연산 수행 등)을 나타냄

2-2) 상태 머신 다이어그램 (State machine Diagram)

  • 객체의 생명주기를 표현
  • 객체의 상태와 상태 간 전이를 모델링

2-3) 유스케이스 다이어그램 (Use Case Diagram)

  • 사용자(Actor)와 시스템의 행위를 표현(상호작용)
  • 시스템의 주요 기능과 사용 사례를 표현

2-4) 시퀀스 다이어그램 (Sequence Diagram)

  • 시간 흐름에 따른 객체 사이의 상호작용을 표현

2-5) 상호작용 개요 다이어그램 (Interaction overview Diagram)

  • 여러 상호작용 다이어그램 사이의 제어 흐름을 표현

2-6) 커뮤니케이션 다이어그램 (Communication Diagram)

  • 객체 간의 메시지 흐름을 중심으로 상호작용을 표현

2-7) 타이밍 다이어그램 (Timing Diagram)

  • 시간에 따른 객체의 상태 변화를 표현



3. 클래스 다이어그램

클래스란? 동일한 속성행위를 수행하는 객체의 집합이다. 클래스 다이어그램(Class Diagram)은 객체지향 설계의 핵심 다이어그램 중 하나로, 시스템 내 클래스들 간의 구조와 관계를 시각적으로 표현한 UML(Unified Modeling Language) 다이어그램이다. 자바와 같은 객체지향 언어 기반으로 설계할 때, 클래스 다이어그램은 설계 단계에서 클래스의 속성, 메서드, 관계 등을 정의하는 데 중요한 역할을 한다.


3-1. 클래스 다이어그램의 구성 요소

1) 클래스 (Class)

  • 정의: 객체를 생성하기 위한 틀(청사진)
  • 표현 방식: 사각형으로 표현되며, 3개의 영역
    • 클래스 이름: 클래스의 이름이 표시됨
    • 속성(Attributes): 클래스의 상태를 나타내는 변수
    • 메서드(Methods): 클래스의 동작(행동)을 나타내는 함수

2) 접근 제어자 (Access Modifiers)

  • 속성과 메서드 앞에 표시되어 가시성을 정의
    • + : Public (공개)
    • - : Private (비공개)
    • # : Protected (상속 관계에서만 접근 가능)
    • ~ : Default (같은 패키지 내에서 접근 가능)

3) 관계 (Relationships)

  • 클래스들 간의 연관성을 나타냄
    • 연관 (Association): 클래스 간에 서로 알고 있는 관계 (→)
    • 집합 (Aggregation): 클래스가 다른 클래스의 일부로 포함되지만 독립적으로 존재 가능 (◇)
    • 구성 (Composition): 클래스가 다른 클래스의 일부로 포함되고, 생명 주기가 포함된 클래스와 연결됨 (◆)
    • 상속 (Inheritance): 부모 클래스와 자식 클래스 간의 관계 (▷)
    • 의존 (Dependency): 한 클래스가 다른 클래스에 의존하는 관계 (—→)


3-2. 관계

관계설명특징표기법
연관
(Association)
두 클래스 간에 서로 알고 있는 관계를 나타냄- 클래스 간 상호작용이 필요
- 단방향 또는 양방향 가능
실선 (→)
일반화
(generalization)
부모 클래스와 자식 클래스 간의 관계 (“IS-A” 관계)- 자식 클래스는 부모 클래스의 속성과 메서드를 상속
- 다형성 지원
- JAVA에서는 상속(Inheritance)
빈 삼각형 (▷)
집합
(Aggregation)
“부분-전체” 관계로, 포함된 객체가 독립적으로 존재 가능- 약한 결합 관계
- 부분(Part)은 전체(Whole)와 생명 주기가 독립적
빈 마름모 (◇)
구성
(Composition)
“부분-전체” 관계로, 포함된 객체의 생명 주기가 전체와 연결됨- 강한 결합 관계
- 전체가 삭제되면 부분도 삭제됨
채워진 마름모 (◆)
의존
(Dependency)
한 클래스가 다른 클래스가 제공하는 기능을 사용할 때 잠시 나타내는 약한 관계- 클래스 간 낮은 결합도 유지
- 일시적으로 메서드 매개변수 등으로 사용
점선 화살표 (—→)
실체화
(Realization)
인터페이스와 이를 구현하는 클래스 간의 관계
- 책임들의 집한인 인터페이스와 이 책임들을 실제로 실현한 클래스들 사이의 관계
- 인터페이스에 정의된 메서드를 구현
- 인터페이스와 구현 클래스 간의 관계를 표현
점선 삼각형 (—▷)

요약

  • 연관: 단순 참조 관계
  • 상속: 부모-자식 관계
  • 집합: 독립적 부분-전체 관계
  • 구성: 강한 부분-전체 관계
  • 의존: 임시적 관계
  • 실체화: 인터페이스와 구현 클래스 간 관계


연관 관계

단순한 형태의 Association Relationship은 클래스들이 서로 연관되어 있음을 나타낸다. 이를 더 쉽게 이해할 수 있도록 아래에 개념을 설명한다. Association(연관)은 두 클래스 간의 기본적인 관계입니다. 하나의 클래스가 다른 클래스와 “알고 있다” 또는 “연결되어 있다”는 의미를 가진다.

1) 단방향 관계 (Unidirectional Relationship)

한쪽 클래스만 다른 클래스를 참조하거나 알고 있는 관계로 관계 탐색이 한 방향으로만 이루어진다. 예시로 <학생(Student) → 수업(Class)> 학생은 자신이 수강하는 수업을 알고 있지만, 수업은 자신을 수강하는 학생들을 모른다.

Class A ---> Class B
//수업 클래스
class Class {
    private final String className;

    public Class(String className) {
        this.className = className;
    }

    public String getClassName() {
        return className;
    }
}

//학생 클래스
class Student {
    private String name;
    private Class enrolledClass;

    public Student(String name, Class enrolledClass) {
        this.name = name;
        this.enrolledClass = enrolledClass;
    }

    public void showClassInfo() {
        System.out.println(name + " 학생은 " + enrolledClass.getClassName() + " 수업에 등록되어 있습니다.");
    }
}

public class UnidirectionalRelationship {
    public static void main(String[] args) {
        Class classLecture = new Class("수학");
        Student student = new Student("김철수", classLecture);

        student.showClassInfo();
    }
}


2) 양방향 관계 (Bidirectional Relationship)

두 클래스가 서로를 참조하거나 알고 있는 관계로 관계 탐색이 양방향으로 이루어진다. 두 클래스가 서로의 정보를 필요로 할 때 사용되며 예시로 <주문(Order) ↔ 고객(Customer)> 고객은 자신이 주문한 것을 알고, 주문은 자신을 주문한 고객들을 알고 있다.

Class A <--> Class B
//주문 클래스
class Order {
    private String orderId;
    private Customer customer;

    public Order(String orderId, Customer customer) {
        this.orderId = orderId;
        this.customer = customer;
    }

    public String getOrderId() {
        return orderId;
    }

    public Customer getCustomer() {
        return customer;
    }
}

//고객 클래스
class Customer {
    private String name;
    private List<Order> orders;

    public Customer(String name) {
        this.name = name;
        this.orders = new ArrayList<>();
    }

    public void addOrder(Order order) {
        orders.add(order);
    }

    public void showOrders() {
        System.out.println(name + "의 주문 목록:");
        for (Order order : orders) {
            System.out.println(" - 주문 ID: " + order.getOrderId());
        }
    }
}

/**
 * 양방향 관계
 */
public class BidirectionalRelationship {
    public static void main(String[] args) {
        Customer customer = new Customer("박웅석");
        Order order1 = new Order("001", customer);
        Order order2 = new Order("002", customer);

        customer.addOrder(order1);
        customer.addOrder(order2);

        customer.showOrders();
    }
}


3) 다대다 관계(Many-to-Many Relationship)

은행(Bank)과 고객(Customer)은 서로 다대다 관계를 가질 수 있다. 은행과 고객의 관계를 다대다 관계로 구현한 예제로 한 고객은 여러 은행에 계좌를 개설할 수 있고, 한 은행도 여러 고객을 가질 수 있다.

3-1) 단방향 다대다 (Unidirectional Many-to-Many)

고객은 자신이 거래하는 은행을 알고 있지만, 은행은 고객을 모름

+----------------+        +----------------+
|    Customer    |        |      Bank      |
+----------------+        +----------------+
| - name         |        | - name         |
| - banks: List  | ------>|                |
+----------------+        +----------------+
class Bank {
    private String name;

    public Bank(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Customer {
    private String name;
    private List<Bank> banks; // 고객이 거래하는 은행들

    public Customer(String name) {
        this.name = name;
        this.banks = new ArrayList<>();
    }

    public void addBank(Bank bank) {
        banks.add(bank); // 고객이 은행과 거래 시작
    }

    public void showBanks() {
        System.out.println(name + " 고객이 거래하는 은행들:");
        for (Bank bank : banks) {
            System.out.println(" - " + bank.getName());
        }
    }
}

/**
 * 단방향 다대다 관계
 */
public class UnidirectionalManyToMany {
    public static void main(String[] args) {
        Bank bank1 = new Bank("국민은행");
        Bank bank2 = new Bank("신한은행");

        Customer customer = new Customer("김철수");
        customer.addBank(bank1);
        customer.addBank(bank2);

        customer.showBanks();
    }
}


3-2) 양방향 다대다 (Bidirectional Many-to-Many)

고객은 자신이 거래하는 은행을 알고, 은행도 자신의 고객들을 알고 있는 구조

+-------------------+        +-------------------+
|    Customer       |<------>|      Bank         |
+-------------------+        +-------------------+
| - name            |        | - name            |
| - banks: List     |        | - customers: List |
+-------------------+        +-------------------+
class Customer {
    private String name;
    private List<Bank> banks; // 고객이 거래하는 은행들

    public Customer(String name) {
        this.name = name;
        this.banks = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public void addBank(Bank bank) {
        banks.add(bank); // 고객이 은행과 거래 시작
        bank.addCustomer(this); // 은행도 고객을 "알게 함"
    }

    public void showBanks() {
        System.out.println(name + " 고객이 거래하는 은행들:");
        for (Bank bank : banks) {
            System.out.println(" - " + bank.getName());
        }
    }
}

class Bank {
    private String name;
    private List<Customer> customers; // 은행이 거래하는 고객들

    public Bank(String name) {
        this.name = name;
        this.customers = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public void addCustomer(Customer customer) {
        customers.add(customer); // 은행에 고객 추가
    }

    public void showCustomers() {
        System.out.println(name + " 은행의 고객들:");
        for (Customer customer : customers) {
            System.out.println(" - " + customer.getName());
        }
    }
}

/**
 * 양방향 다대다 관계
 */
public class BidirectionalManyToMany {
    public static void main(String[] args) {
        Bank bank1 = new Bank("국민은행");
        Bank bank2 = new Bank("신한은행");

        Customer customer1 = new Customer("김철수");
        Customer customer2 = new Customer("이영희");

        // 고객이 은행과 거래 시작
        customer1.addBank(bank1);
        customer1.addBank(bank2);
        customer2.addBank(bank1);

        // 출력 확인
        customer1.showBanks();
        bank1.showCustomers();
    }
}


3-3) 연관 클래스 (Association Class)

연관 클래스를 이용한 다대다 관계는 두 클래스 사이에 중간 역할을 하는 클래스(연관 클래스)를 추가하여 관계를 명확히 하고, 추가적인 정보를 저장하거나 관리할 수 있도록 설계한다.
고객 ↔ 은행 관계를 연관 클래스인 Account가 중간에서 고객과 은행의 관계를 나타낸다. 결국은 Customer과 Bank은 N:N 관계로 테이블 구조에서도 연관테이블이 존재하는 구조이다.

+----------------+       +----------------+       +----------------+
|    Customer    |       |    Account     |       |      Bank      |
+----------------+       +----------------+       +----------------+
| - name         |       | - accountId    |       | - name         |
|                |<----->| - balance      |<----->|                |
|                |       | - openedDate   |       |                |
+----------------+       +----------------+       +----------------+
// 고객 클래스
class Customer {
    private String name;
    private List<Account> accounts; // 고객의 계좌 목록

    public Customer(String name) {
        this.name = name;
        this.accounts = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public void addAccount(Account account) {
        accounts.add(account);
    }

    public void showAccounts() {
        System.out.println(name + " 고객의 계좌:");
        for (Account account : accounts) {
            System.out.println(" - 계좌 번호: " + account.getAccountId() + ", 잔액: " + account.getBalance());
        }
    }
}

// 은행 클래스
class Bank {
    private String name;
    private List<Account> accounts; // 은행의 계좌 목록

    public Bank(String name) {
        this.name = name;
        this.accounts = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public void addAccount(Account account) {
        accounts.add(account);
    }

    public void showAccounts() {
        System.out.println(name + " 은행의 계좌:");
        for (Account account : accounts) {
            System.out.println(" - 계좌 번호: " + account.getAccountId() + ", 고객: " + account.getCustomer().getName());
        }
    }
}

// 연관 클래스: Account (고객과 은행을 연결)
class Account {
    private String accountId;
    private double balance;
    private Date openedDate;
    private Customer customer; // 연관된 고객
    private Bank bank;         // 연관된 은행

    public Account(String accountId, double balance, Customer customer, Bank bank) {
        this.accountId = accountId;
        this.balance = balance;
        this.openedDate = new Date(); // 현재 날짜로 계좌 개설일 설정
        this.customer = customer;
        this.bank = bank;

        // 관계 설정
        customer.addAccount(this);
        bank.addAccount(this);
    }

    public String getAccountId() {
        return accountId;
    }

    public double getBalance() {
        return balance;
    }

    public Customer getCustomer() {
        return customer;
    }

    public Bank getBank() {
        return bank;
    }
}

/**
 * 연관 클래스를 이용한 다대다 관계
 */
public class AssociationClassManyToMany {
    public static void main(String[] args) {
        // 은행 생성
        Bank bank1 = new Bank("국민은행");
        Bank bank2 = new Bank("신한은행");

        // 고객 생성
        Customer customer1 = new Customer("김철수");
        Customer customer2 = new Customer("이영희");

        // 계좌 생성 (연관 클래스 사용)
        new Account("001", 1000.0, customer1, bank1);
        new Account("002", 2000.0, customer1, bank2);
        new Account("003", 1500.0, customer2, bank1);

        // 출력 확인
        customer1.showAccounts();
        bank1.showAccounts();
    }
}



일반화(상속) 관계

일반화 관계(Generalization Relationship)는 객체 지향 설계에서 부모 클래스(상위 클래스)자식 클래스(하위 클래스) 간의 “is-a” 관계를 나타냅니다. 그래서 일반화 관계를 상속 관계라고 한다. 일반화 관계공통된 속성동작(메서드)을 상위 클래스에서 정의하고, 이를 하위 클래스가 상속받아 사용하는 관계이다. 하위 클래스는 상위 클래스의 모든 속성과 동작을 물려받으며(다형성), 필요에 따라 확장(override 또는 추가 정의)할 수 있다.

          +---------------+
          |    Animal     |
          +---------------+
          | - name        |
          | + eat()       |
          +---------------+
                 ▲
                 |
      +----------+----------+
      |                     |
+-----------+        +------------+
|   Dog     |        |    Cat     |
+-----------+        +------------+
| + bark()  |        | + meow()   |
+-----------+        +------------+
// 상위 클래스
class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

// 하위 클래스 1
class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    public void bark() {
        System.out.println(name + " is barking.");
    }
}

// 하위 클래스 2
class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    public void meow() {
        System.out.println(name + " is meowing.");
    }
}

/**
 * 일반화 관계
 */
public class GeneralizationRelationship {
    public static void main(String[] args) {
        // 상위 클래스 타입으로 하위 클래스 참조
        Animal dog = new Dog("콜라");
        Animal cat = new Cat("사이다");

        // 상위 클래스의 메서드 호출
        dog.eat();
        cat.eat();

        // 하위 클래스의 메서드 호출
        if (dog instanceof Dog) {
            ((Dog) dog).bark();
        }

        if (cat instanceof Cat) {
            ((Cat) cat).meow();
        }
    }
}


◼︎ 일반화(상속) 관계연관(포함) 관계의 차이

1) 연관 관계 (Association Relationship)

연관 관계는 클래스 간의 포함(Composite)를 표현하며, "has-a" 관계로 한 클래스가 다른 클래스를 소유하거나 참조하는 관계이다. UML 다이어그램에서는 직선으로 표현한다.

  • :
    • Car has an Engine (자동차는 엔진을 가지고 있다)
    • Student has a LibraryCard (학생은 도서관 카드를 가지고 있다)

2) 일반화 관계 (Generalization Relationship)

일반화 관계는 클래스 간의 상속(Inheritance) 관계를 표현하며, "is-a" 관계로 상위 클래스(부모 클래스)의 속성과 메서드를 하위 클래스(자식 클래스)가 상속받는 관계이다. UML 다이어그램에서는 빈 삼각형 화살표로 표현한다.

  • :
    • Dog is an Animal (강아지는 동물의 일종)
    • Car is a Vehicle (자동차는 차량의 일종)



집합 관계

집합 관계(Aggregation Relationship)has-a” 관계의 한 유형으로, 클래스 간의 전체(Whole)와 부분(Part) 간의 관계를 나타낸다. 전체와 부분은 느슨하게 연결되어 있으며, 부분 객체는 전체 객체가 사라져도 독립적으로 존재할 수 있다. 다중성으로 일반적으로 1:N 관계로 사용된다.
UML 클래스 다이어그램에서 빈 다이아몬드로 표시하며, Team은 전체(Whole)이고 Player는 부분(Part)으로 전체가 부분을 느슨하게 소유하는 관계이다.

+---------+             +---------+
|   Team  |<>---------->|  Player |
+---------+             +---------+
| - name  |             | - name  |
+---------+             +---------+
// Player 클래스 (부분 객체)
class Player {
    private String name;

    public Player(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// Team 클래스 (전체 객체)
class Team {
    private String name;
    private List<Player> players; // 팀에 속한 선수들

    public Team(String name) {
        this.name = name;
        this.players = new ArrayList<>();
    }

    public void addPlayer(Player player) {
        players.add(player); // 선수 추가
    }

    public void showPlayers() {
        System.out.println(name + " 팀의 선수 목록:");
        for (Player player : players) {
            System.out.println(" - " + player.getName());
        }
    }
}

/**
 * 집합 관계
 */
public class AggregationRelationship {
    public static void main(String[] args) {
        // 선수 생성
        Player player1 = new Player("김으장");
        Player player2 = new Player("임수정");

        // 팀 생성
        Team team = new Team("FC 서울");

        // 팀에 선수 추가
        team.addPlayer(player1);
        team.addPlayer(player2);

        // 팀 정보 출력
        team.showPlayers();
    }
}



구성 관계

구성 관계(Composition Relationship)는 객체 지향 설계에서 has-a를 나타냅니다. 구성 관계는 집합 관계와 유사하지만, 구성 관계전체 객체가 부분 객체를 강하게 소유하고 있는 형태이다. 이는 전체(Whole)가 존재하지 않으면 부분(Part)도 존재할 수 없는 관계를 의미한다.
UML 다이어그램에서 채워진 다이아몬드(●)로 표시되며, House는 전체(Whole)이고 Room은 부분(Part)이다.

+--------+               +--------+
| Hotel  |●------------->| Room   |
+--------+               +--------+
| - name |               | - name |
+--------+               +--------+
// 객실 클래스 (부분 객체)
class Room {
    private String name;

    public Room(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// 호텔 클래스 (전체 객체)
class Hotel {
    private String name;
    private List<Room> rooms;

    public Hotel(String name) {
        this.name = name;
        this.rooms = new ArrayList<>();
    }

    public void addRoom(String roomName) {
        rooms.add(new Room(roomName)); // 호텔이 객실을 소유
    }

    public void showRooms() {
        System.out.println(name + "의 객실 목록:");
        for (Room room : rooms) {
            System.out.println(" - " + room.getName());
        }
    }
}

/**
 * 구성 관계
 */
public class CompositionRelationship {
    public static void main(String[] args) {
        // 호텔 생성
        Hotel hotel = new Hotel("포포인츠호텔");

        // 객실 추가
        hotel.addRoom("711-싱글스탠다드");
        hotel.addRoom("856-더블스탠다드");

        hotel.showRooms();
    }
}


◼︎ 집합관계와 구성관계

1) 집합 관계(Aggregation Relationship)

  1. 느슨한 결합
    • 부분 객체는 전체 객체의 생명 주기와 독립적
    • 전체 객체가 삭제되더라도 부분 객체는 삭제되지 않음
    • 예)
      • 팀이 해체되어도 개별 선수들은 여전히 존재할 수 있음
      • 도서관이 없어져도 책은 여전히 존재할 수 있음
  2. “has-a” 관계의 특수화
    • 연관 관계(Association Relationship)와 비슷하지만, 전체와 부분 간의 소유권이 강조
  3. UML 표현
    • UML 클래스 다이어그램에서 빈 다이아몬드로 표시
  4. 다중성
    • 일반적으로 1:N 관계로 표현되며, 전체 객체는 여러 부분 객체를 포함

2) 구성 관계(Composition Relationship)

  1. 강한 결합
    • 부분 객체는 전체 객체의 생명 주기에 종속
    • 전체 객체가 삭제되면 부분 객체도 삭제
    • 예)
      • 집이 파괴되면 방도 사라짐
      • 자동차가 없어지면 엔진도 존재할 수 없음
  2. “has-a” 관계의 특수화
    • 구성 관계는 집합 관계와 유사하지만, 소유권이 강하게 유지
  3. UML 표현
    • UML 다이어그램에서 채워진 다이아몬드(●)로 표시
  4. 부분 객체의 독립성 없음
    • 부분 객체는 전체 객체 없이 독립적으로 존재할 수 없음



의존 관계

의존 관계(Dependency Relationship)는 객체 지향 설계에서 한 클래스가 다른 클래스에 의존하는 관계를 나타낸다. 이 관계는 “사용 관계”라고도 하며, 한 클래스가 다른 클래스의 변화에 영향을 받는 관계이다.

의존 관계는 UML에서 한 클래스가 다른 클래스에 의존적으로 사용하는 관계를 나타낸다. 한 클래스가 다른 클래스의 메서드를 호출하거나 객체를 생성하여 임시로 사용하는 관계를 의미한다. 그래서 의존 관계는 클래스 간의 약한 결합(loose coupling)을 하고 있다.


• 의존 관계의 특징

1) 임시적 관계

  • 의존 관계는 한 클래스가 다른 클래스의 객체나 메서드를 일시적으로 사용할 때 발생
  • 관계가 지속적이지 않으며, 한 번의 호출이나 특정 작업이 끝나면 종료

2) 약한 결합

  • 의존 대상 클래스의 생명 주기에 직접 영향을 미치지 않으며, 독립적으로 존재할 수 있음

3) 변화에 민감

  • 의존 대상 클래스가 변경되면, 의존하는 클래스의 동작에도 영향을 줄 수 있음


UML 클래스 다이어그램에서 점선 화살표(—->)로 표현되며, 화살표는 의존하는 클래스에서 의존 대상 클래스를 가리킨다. Order(주문) 클래스는 고객 정보를 사용하여 주문을 처리하지만, OrderCustomer는 생명 주기를 공유하지 않는다.

+-----------------+      +----------------+
|     Order       |----->|    Customer    |
+-----------------+      +----------------+
| - id            |      | - name         |
| + processOrder()|      | + getName()    |
+-----------------+      +----------------+
// Customer 클래스
class Customer {
    private String name;

    public Customer(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// Order 클래스
class Order {
    private int id;

    public Order(int id) {
        this.id = id;
    }

    // Customer를 의존적으로 사용하는 메서드
    public void processOrder(Customer customer) {
        System.out.println("Processing order ID: " + id + " for customer: " + customer.getName());
    }
}

/**
 * 의존 관계
 */
public class DependencyRelationship {
    public static void main(String[] args) {
        // Customer 객체 생성
        Customer customer = new Customer("송양해");

        // Order 객체 생성
        Order order = new Order(123);

        // Order가 Customer를 의존적(임시)으로 사용
        // 한 클래스가 다른 클래스의 객체나 메서드를 임시로 참조 하는 관계
        order.processOrder(customer);
    }
}



실체화 관계

실체화 관계(Realization Relationship)는 UML에서 인터페이스(Interface)와 이를 구현하는 클래스(Class) 또는 구성 요소(Component) 간의 관계를 나타낸다. 이는 인터페이스에서 정의한 책임을 객체에서 처리해야 할 일이다. 즉, 구현체가 인터페이스에서 정의한 동작(method)을 구현(implement)하도록 보장하는 관계이다. 그레서 인터페이스와 구현체 간의 can-do” 관계이다.

UML 다이어그램에서 실체화 관계를 명확히 표현하면 클래스 간의 의존성과 책임을 쉽게 파악할 수 있다. 이를 통해 인터페이스의 정의와 구현을 분리하고, 객체 지향 설계의 핵심인 다형성과 유연성을 제공한다.

UML에서 점선 삼각형 화살표(-----▷)<인터페이스>-----▷<클래스(구현체)>로 표현한다. Vehicle(인터페이스)와 이를 구현하는 Car(클래스)Bike(클래스) UML 다이어그램이다.

        +----------------+
        |  <<interface>> |
        |    Vehicle     |
        +----------------+
        | + start()      |
        | + stop()       |
        +----------------+
                ▲
                |
      ---------------------
      |                   |
+----------+        +----------+
|  Car     |        |  Bike    |
+----------+        +----------+
| + start()|        | + start()|
| + stop() |        | + stop() |
+----------+        +----------+
// 인터페이스 (Vehicle)
interface Vehicle {
     //인터페이스에서 정의한 책임(메서드)
    void start();
    void stop();
}

// 구현 클래스 1 (Car)
// 인터페이스에서 정의한 메서드를 구현
class Car implements Vehicle {
    @Override
    public void start() {
        System.out.println("Car is starting.");
    }

    @Override
    public void stop() {
        System.out.println("Car is stopping.");
    }
}

// 구현 클래스 2 (Bike)
// 인터페이스에서 정의한 메서드를 구현
class Bike implements Vehicle {
    @Override
    public void start() {
        System.out.println("Bike is starting.");
    }

    @Override
    public void stop() {
        System.out.println("Bike is stopping.");
    }
}

// 메인 클래스
public class RealizationRelationship {
    public static void main(String[] args) {
        // Vehicle 타입으로 Car와 Bike 객체를 참조
        Vehicle car = new Car();
        Vehicle bike = new Bike();

        //인터페이스에서 정의한 책임을 처리
        car.start();
        car.stop();

        bike.start();
        bike.stop();
    }
}