subindev 님의 블로그

[Flutter] #6 Dart 기본 문법 연관관계와 믹스인 본문

앱개발/Flutter

[Flutter] #6 Dart 기본 문법 연관관계와 믹스인

subindev 2025. 1. 7. 14:05

Dart 프로그래밍 기초

 

1. 컴퍼지션과 집합관계


컴퍼지션(Composition)과 집합(Aggregation)은 객체지향 프로그래밍에서 객체 간의 관계를 표현하는 방법 중 두 가지로, 둘 다 부분-전체 관계를 나타냅니다. 하지만 이 두 관계는 강도의존성에서 차이가 있습니다. 아래에서 각 개념을 자세히 설명하겠습니다.

 

컴퍼지션(Composition)이란 ? 

컴퍼지션은 "부분-전체" 관계로, 한 객체가 다른 객체를 포함하는 형태입니다. 이 관계는 객체 간에 강한 의존성을 의미합니다. 즉, 포함된 객체는 포함하는 객체에 생명주기가 의존하며, 포함된 객체는 포함하는 객체가 없으면 존재할 수 없습니다.

예를 들어, 자동차(Car) 객체는 엔진(Engine) 객체를 포함하는 관계입니다. 자동차가 파괴되면 그 안에 포함된 엔진도 함께 파괴됩니다.

 

  • 강한 관계: 포함된 객체(부분 객체)는 포함하는 객체(전체 객체)가 파괴되면 함께 파괴됩니다.
  • 부분-전체 관계: 전체 객체가 여러 개의 부분 객체를 포함합니다.
  • 구성 요소 교체 가능: 포함된 객체는 다른 객체로 쉽게 교체될 수 있습니다.
class Engine {
  void start() {
    print("Engine started");
  }
}

class Car {
  final Engine engine;  // 자동차는 엔진을 소유

  Car() : engine = Engine();  // 엔진 객체 생성

  void startCar() {
    engine.start();
    print("Car started");
  }
}

void main() {
  Car myCar = Car();
  myCar.startCar();  // 자동차와 엔진이 함께 시작됨
}

 

집합(Set)이란 ?

집합은 수학적 개념으로, 특정 요소들의 모임입니다. 집합의 각 요소는 서로 독립적이고, 집합 내에서 각 요소 간의 관계는 "소속" 관계로 정의됩니다. 집합은 단순히 특정 요소들이 존재한다는 것을 나타내며, 각 요소는 그 집합에 속하는지 여부만을 가지고 있습니다.

집합에서 중요한 점은 요소 간의 독립성입니다. 즉, 집합의 각 원소는 독립적으로 존재할 수 있으며, 집합을 구성하는 원소가 다른 원소에 의존하지 않습니다. 집합을 변경한다고 해서 그 안에 속한 요소들이 변경되는 것은 아닙니다.

특징:

  • 독립성: 집합의 원소들은 서로 독립적입니다.
  • 소속 관계: 원소들이 집합에 속하는지 여부를 정의합니다.
  • 변경되지 않는 원소: 집합의 원소는 집합에 속한다고 해서 다른 원소와의 관계에서 변화가 없습니다.
class Book {
  final String title;

  Book(this.title);
}

class Library {
  final List<Book> books = [];  // 도서관은 책을 참조

  void addBook(Book book) {
    books.add(book);
  }
}

void main() {
  Book book1 = Book("The Great Gatsby");
  Book book2 = Book("1984");

  Library library = Library();
  library.addBook(book1);
  library.addBook(book2);

  print("Library contains ${library.books.length} books");
}

컴퍼지션과 집합의 관계

컴퍼지션과 집합은 모두 구성 요소를 포함한다는 점에서 유사하지만, 두 관계의 본질은 다릅니다.

  • 컴퍼지션부분-전체 관계로, 전체 객체가 특정 부분 객체를 소유하고 그 객체들의 생명주기가 서로 밀접하게 연결됩니다.
  • 집합소속 관계로, 특정 객체들이 모여 하나의 집합을 구성하지만, 각 객체는 독립적으로 존재하며 서로 간에 생명주기가 연결되지 않습니다.

 

사나리오1 - 연관관계 컴포지션 관계와 집합관계

class Engine {
  final String type;

  Engine(this.type);

  void startEngine() {
    print('${type} 엔진이 시동됩니다');
  }
}

//
class Car {
  final Engine engine;

  // Car(this.engine);
  // 생성자 코드이다. 1. 축약버전 --> 생성자 바디부분을 생략 했다.
  // Car(String enginType) : engine = Engine(enginType);
  // 생성자 코드이다. 2.
  Car(String enginType) : engine = Engine(enginType) {
    print('생성자 호출 시 내부 스택 메모리가 호출 된다.');
  }

  void startCar() {
    engine.startEngine();
    print("차가 출발 합니다.");
  }
}

////////////////////////////////////////////
// 집합 관계를 만들어 보자.

class Employee {
  final String name;

  Employee(this.name);

  void displayEmployeeInfo() {
    print('직원에 이름 : ${name}');
  }
}

class Department {
  final String deptName;
  final List<Employee> employees;

  Department(this.deptName) : employees = [] {
    print('=== Department 생성자 내부 스택 호출 ===');
  }

  void addEmployee(Employee emp) {
    employees.add(emp);
  }

  void displayDepartmentInfo() {
    print("-------------------");
    print("부서에 이름 : ${deptName}");
    for (var emp in employees) {
      emp.displayEmployeeInfo();
    }
  }
}

void main() {
  // Engine v8Engine = Engine('v8');
  // Car car1 = Car(v8Engine);
  //////////////////////////////
  Car car1 = Car('v8');
  // 누군가 참조하고 있지 않다면 GC 대상이 된다.
  print('-------------------');

  Department department1 = Department('개발부');
  Department department2 = Department('디자인부서');
  Employee emp1 = Employee('홍길동');
  Employee emp2 = Employee('김철수');
  Employee emp3 = Employee('야스오');

  department1.addEmployee(emp1);
  department1.addEmployee(emp2);
  department2.addEmployee(emp3);

  department1.displayDepartmentInfo();
}

 

2. Mixin


Dart 에서 클래스의 코드를 여러 클래스에 계층에서 재사용할 수 있도록 해주는 코드 조각이다. Mixin을 사용하게 되면 다중 상속의 문제를 해결할 수 있고 컴퍼지션을 사용하지 않고 다른 클래스의 코드를 재사용 할 수 있다.
하지만 이는 인스턴스화 시킬 수 없다.

// 여러 계층에서 코드를 재사용할 수 있도록 하는 코드 조각이다.

mixin Engine {
  int power = 5000;
}

mixin Wheel {
  String wheelName = '4륜 구동 바퀴';
}

class BMW with Engine, Wheel {}

void main() {
  // 인스턴스화 시킴
  BMW bm1 = BMW();
  print(bm1.power);
  print(bm1.wheelName);

  // 상속구조를 사용하면 단일 상속만 허용한다. 믹스인 사용하면 여러 계층에서
  // 코드의 조각들을 편하게 가져올 수 있다.
  // 단 믹스인 maxin Engine 이 인스턴스화 되는 것은 아니다. !!!!
  // Wheel wheel1 = Wheel(); 믹스인은 인스턴스화 시킬 수 없다.
  
}

 

만약 믹스인을 인스턴스화 시키고 싶다면 ?  믹스인 클래스로 만든다

// 믹스인을 인스턴스화 시킬 때 사용하는 문법
mixin class Engine {
  int power = 5000;
}

mixin class Wheel {
  String wheelName = '4륜 구동 바퀴';
}

class BMW with Engine, Wheel {}

void main() {
  BMW b = BMW();
  Engine e = Engine(); // 믹스인 클래스는 인스턴스화 가능하다.
  Wheel w = Wheel();
  print(b.power);
}

 


Quiz )

 

문제 1: 컴포지션 관계 설계하기

 

문제 설명

House와 Room 클래스 간의 관계를 컴포지션(Composition) 관계로 설계하세요. House 클래스는 Room 객체를 포함하며, House 객체가 소멸될 때 Room 객체도 함께 소멸되어야 합니다. House 클래스에는 주소(address)와 방(room) 정보를 포함하고, Room 클래스에는 방 번호(roomNumber)를 포함합니다. 또한, House 객체의 정보를 출력하는 displayHouseInfo() 메서드를 구현하세요.

 

문제 2: 집합관계(Aggregation) 설계하기

문제 설명

Apartment와 Resident 클래스 간의 관계를 **집합관계(Aggregation)**로 설계하세요. Apartment 클래스는 여러 Resident 객체를 포함하며, Apartment 객체가 소멸되더라도 Resident 객체들은 독립적으로 존재할 수 있어야 합니다. Apartment 클래스에는 건물 이름(buildingName)과 거주자 리스트(residents)를 포함하고, Resident 클래스에는 거주자 이름(name)을 포함합니다. 또한, Apartment 객체의 정보를 출력하는 displayApartmentInfo() 메서드를 구현하세요.