Flutter

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

ekkkang 2025. 1. 7. 18:31

 

시나리오 코드 - 1 연관 관계 —> 컴포지션 관계와 집합관계

OOP에서 객체 간의 관계를 정의하는 개념들

 

1. 연관관계

  - 객체들이 서로 연결되어 있는 일반적인 관계

 

  - 컴포지션(포함 관계)

     - 부분-전체 관계 중에서 생명주기가 밀접하게 연관된 강한 소유 관계를 의미합니다.

     - 전체 객체가 소멸될 때 부분 객체도 함께 소멸 됩니다.

     - 예시 : 차와 엔진 클래스도 컴포지션 관계 (ex 사람과 심장)

     - [강한 소유 관계]

 

  - 집합관계

    - 부분-전체 관계 중에서 전체와 부분의 생명주기가 독립적인 관계를 말합니다.

    - 전체 객체가 소멸될 때 부분 객체는 독립적으로 존재할 수 있습니다.

    - 예시 : 부서와 직원 클래스를 생각할 수 있습니다.

    - 부서 객체가 소멸되더라도 직원 객체는 계속 존재할 수 있습니다.

    - [약한 소유 관계]

 

2. 이니셜라이저 리스트( : )

  - 상속 관계뿐만 아니라 연관관계(집합관계 및 컴포지션 관계)에서도 클래스의 필드 초기화 및 객체 생성에 유용하게 사용할 수 있다.

 

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();
  department2.displayDepartmentInfo();
}

 

 

시나리오 코드 - 2 믹스인 사용

Mixin 란?
  • Dart 에서 클래스의 코드를 여러 클래스의 계층에서 재사용할 수 있도록 해주는 코드 조각입니다.
  • Mixin을 사용하게 되면 다중 상속의 문제를 해결할 수 있고 컴포지션을 사용하지 않고 다른 클래스의 코드를 재사용할 수 있습니다. 
  • 상속구조를 사용하면 단일 상속만 허용하는 반면, 믹스인 사용하면 여러 계층에서 코드의 조각들을 편하게 가져올 수 있다.
  • 단, 믹스인은 mixin Engine이 인스턴스화 되는 것은 아닙니다!!!
  • Wheel wheel1 = Wheel();  믹스인은 인스턴스화 시킬 수 없다.

 

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);

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

 

 

시나리오 코드 - 3 믹스인을 만약 인스턴스화 시키고 싶다면 어떻게 할까?

// 믹스인을 인스턴스화 시킬 때 사용하는 문법

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);
}

 


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

문제 설명

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

class Room {
  int roomNumber;
  Room(this.roomNumber);
}

class House {
  final String address;
  final List<Room> rooms;

  House(this.address) : rooms = [] {}

  void addRoom(int roomNumber) {
    Room room = Room(roomNumber);
    rooms.add(room);
  }

  void displayHouseInfo() {
    print("--------------------");
    print("주소 : ${address}");
    for (var room in rooms) {
      print("방 번호 : ${room.roomNumber}");
    }
  }
}

void main() {
  House house = House("사상");
  house.addRoom(302);
  house.displayHouseInfo();
}

 

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

문제 설명

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

class Resident {
  final String name;

  Resident(this.name);

  void displayResidentInfo() {
    print('거주자 이름 : ${name}');
  }
}

class Apartment {
  final String buildingName;
  final List<Resident> residents;

  Apartment(this.buildingName) : residents = [] {
    print('=== Apartment 생성자 내부 스택 호출 ===');
  }

  void addResident(Resident res) {
    residents.add(res);
  }

  void displayApartmentInfo() {
    print("---------------------");
    print("건물 이름 : ${buildingName}");
    for (var res in residents) {
      res.displayResidentInfo();
    }
  }
}

void main() {
  Apartment apartment1 = Apartment('건물1');
  Apartment apartment2 = Apartment('건물2');
  Resident res1 = Resident('거주자1');
  Resident res2 = Resident('거주자2');
  Resident res3 = Resident('거주자3');

  apartment1.addResident(res1);
  apartment1.addResident(res2);
  apartment2.addResident(res3);

  apartment1.displayApartmentInfo();
  apartment2.displayApartmentInfo();
}