1. 연관관계란?
연관관계는 객체 간의 상호작용과 관계를 나타냅니다. 연관관계는 소유권과 생명주기에 따라 다음과 같이 분류됩니다.
1.1 컴포지션 관계 (Composition)
- 강한 소유 관계:
부분-전체 관계에서 **전체(Whole)**와 **부분(Part)**의 생명주기가 밀접하게 연결되어 있습니다. - 특징:
전체 객체가 소멸되면 부분 객체도 함께 소멸됩니다. - 예시:
- 자동차와 엔진 (차가 망가지면 엔진도 사용할 수 없음).
- 사람과 심장 (사람이 죽으면 심장도 멈춤)
1.2 집합 관계 (Aggregation)
- 약한 소유 관계:
부분-전체 관계에서 전체와 부분의 생명주기가 독립적입니다. - 특징:
전체 객체가 소멸되어도 부분 객체는 독립적으로 존재할 수 있습니다. - 예시:
- 부서와 직원 (부서가 없어져도 직원은 다른 부서로 이동 가능).
- 회사와 직원 (회사가 망해도 직원은 다른 회사에서 일할 수 있음).
2. 컴포지션 관계 구현
컴포지션 관계를 Dart로 구현하려면 전체 객체가 부분 객체를 소유하는 형태로 설계합니다. 아래 코드는 Car 클래스가 Engine 객체를 소유하며, 차가 소멸되면 엔진도 함께 소멸되는 관계를 나타냅니다.
class Engine {
final String type;
Engine(this.type);
}
class Car {
final Engine engine;
Car(this.engine);
}
void main() {
Engine v8engine = Engine('v8');
Car car1 = Car(v8engine);
}
💡 그냥 엔진을 받아서 생성자 초기화를 해주는 식으로 설계하면 메인 함수에서 차를 인스턴스화시킬 때 엔진을 만들어 직접 넣어주어야 함
개선된 컴포지션 관계 구현
class Engine {
final String type;
Engine(this.type);
}
class Car {
final Engine engine;
// 생성자 코드이다 1. 축약 버전 --> 생성자 바디 부분을 생략했다.
Car(String engineType) : engine = Engine(engineType);
// 생성자 코드이다 2. 비축약 버전
Car(String engineType) : engine = Engine(engineType) {
print('생성자 호출시 내부 스택 메모리가 호출된다.');
}
}
void main() {
// Engine v8engine = Engine('v8');
// Car car1 = Car(v8engine);
Car car1 = Car('v8');
// 누군가 참조하고 있지 않다면 gc(garbage collection) 대상이 된다.
}
💡 이니셜라이저 리스트를 활용해 Car 클래스를 호출하여 객체로 만들 때 엔진의 이름을 넣어주면 생성자 내부에서 엔진을 호출해서 만들어준다
집합 관계 구현
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() {
Department dept1 = Department('개발팀');
Department dept2 = Department('디자인팀');
Employee emp1 = Employee('홍길동');
Employee emp2 = Employee('김철수');
Employee emp3 = Employee('야스오');
dept1.addEmployee(emp1);
dept1.addEmployee(emp2);
dept2.addEmployee(emp3);
dept1.displayDepartmentInfo();
dept2.displayDepartmentInfo();
// 부서가 소멸되어도 직원은 독립적으로 존재
}
객체 지향 프로그래밍에서 연관관계는 효과적인 설계를 가능하게 합니다.
Dart는 상속과 조합 외에도 Mixin 문법을 통해 컴포지션 없이도 여러 클래스에서
코드를 재사용할 수 있는 도구를 제공합니다.
이제 Dart의 Mixin 문법을 알아보겠습니다.
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);
// 상속구조를 사용하면 단일 상속만 허용한다. 믹스인을 사용하면 여러 계층에서
// 코드의 조각들을 편하게 가져올 수 있다.
// 단, 믹스인은 Mixin Engine이 인스턴스화 되는 것은 아니다.!!!
// Wheel wheel1 = Wheel(); 믹스인은 인스턴스화 시킬 수 없다.
}
mixin을 인스턴스화 시키고 싶다면?
// 믹스인을 인스턴스화 시킬 때 사용하는 문법
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);
}
객체 지향 프로그래밍(OOP)에서 연관관계는 객체 간의 관계를 표현하며, Dart에서는 컴포지션과 집합 관계로 강한 소유와 약한 소유를 구현할 수 있습니다.
또한, Dart의 Mixin 문법은 연관관계 없이도 코드 조각을 여러 클래스에서 재사용할 수 있는 방법을 제공하며, 다중 상속의 단점을 피하면서 유연한 설계를 가능하게 합니다.
연관관계와 Mixin을 상황에 맞게 활용하면 더 효율적이고 깔끔한 OOP 코드를 작성할 수 있습니다.
🔗 Dart의 객체 지향 패러다임에 대해 더 알고 싶다면?
https://seohong.tistory.com/28
dart 상속과 super
상속과 다형성이란? 상속은 부모 클래스의 속성과 메서드를 자식 클래스가 물려받는 기능다형성은 자식 클래스 객체를 부모 클래스 타입으로 참조할 수 있는 능력을 의미※ 추가설명만약 BMW
seohong.tistory.com
'Flutter > Dart 언어' 카테고리의 다른 글
Stack 위젯 테스트 코드 (2) | 2025.01.13 |
---|---|
[Dart] dart의 추상 클래스 (0) | 2025.01.08 |
[Dart] 상속과 super (0) | 2025.01.08 |
[Dart] Sound Null Safety (0) | 2025.01.08 |
[Dart] 클래스와 인스턴스 (0) | 2025.01.04 |