[Flutter] Dart 기본 문법 #6 연관관계와 믹스인
시나리오 코드 - 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: 컴포지션 관계 설계하기
문제 설명
House와 Room 클래스 간의 관계를 컴포지션(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) 설계하기
문제 설명
Apartment와 Resident 클래스 간의 관계를 **집합관계(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();
}