Dartクラス&OOP
コンストラクタ5種、継承、インターフェース、Mixin、ファクトリパターン
Dartは完全なオブジェクト指向言語ですべてがオブジェクトです。基本、名前付き、初期化リスト、const、factoryの5種コンストラクタを提供し、extendsで継承、implementsでインターフェース、withでMixinを実現します。ファクトリコンストラクタはfromJson直列化、シングルトン、条件別サブクラス返却など実戦で頻繁に使います。
1. 클래스 기본 구조
Dart에서 모든 것은 객체이며, 모든 객체는 클래스의 인스턴스입니다. 클래스는 필드, 생성자, 메서드, getter/setter로 구성됩니다.
class Person { String name; int age; Person(this.name, this.age); void introduce() { print('안녕하세요, 저는 $name이고 $age살입니다.'); } bool get isAdult => age >= 18; set setAge(int newAge) { if (newAge >= 0) { age = newAge; } }}2. Private 멤버 (_ 언더스코어)
언더스코어(_)로 시작하는 멤버는 라이브러리(파일) 수준 private입니다. public getter로 안전하게 노출합니다.
class Person { String name; int _age; Person(this.name, this._age); int get age => _age; void _privateMethod() { print('내부에서만 호출 가능'); } void publicMethod() { _privateMethod(); }}3. 생성자 5종
3-1. 기본 생성자 (shorthand)
this.name으로 파라미터를 자동 할당
Person(this.name, this.age);3-2. 이름 있는 생성자 (Named Constructor)
같은 클래스에 여러 생성자를 만들 수 있습니다.
Person.guest() { name = '손님'; age = 0; } Person.child(this.name) { age = 10; } Person.adult(this.name) { age = 18; } // 사용: Person.guest(), Person.child('아이'), Person.adult('성인')3-3. 초기화 리스트 (Initializer List)
생성자 본문 실행 전 : 뒤에서 필드 초기화. final 필드에 유용.
Person(this.name, this.age) : birthDate = DateTime.now().subtract(Duration(days: 365 * age)); Person.custom(String userName, int userAge) : name = userName.toUpperCase(), age = userAge > 0 ? userAge : 0, birthDate = DateTime.now().subtract(Duration(days: 365 * userAge));3-4. 상수 생성자 (Const Constructor)
불변 객체. 동일 값의 const 인스턴스는 같은 객체를 참조.
class ImmutablePoint { final int x; final int y; const ImmutablePoint(this.x, this.y); } var p1 = const ImmutablePoint(1, 2); var p2 = const ImmutablePoint(1, 2); print(identical(p1, p2)); // true3-5. 리다이렉팅 생성자 (Redirecting)
: this(...)로 다른 생성자에 위임.
Person.adult(String name) : this(name, 18); Person.child(String name) : this(name, 10); Person.fromJson(Map<String, dynamic> json) : this(json['name'] as String, json['age'] as int);4. 생성자 비교 테이블
| 종류 | 문법 | 용도 |
|---|---|---|
| 기본 | Person(this.name) | 자동 초기화 |
| 이름 있는 | Person.guest() | 여러 초기화 |
| 초기화 리스트 | : birthDate = ... | 파생값/final |
| 상수 | const Person() | 불변 객체 |
| 리다이렉팅 | : this(name, 18) | 생성자 위임 |
| 팩토리 | factory Logger(name) | 캐싱/싱글톤 |
5. 팩토리 생성자 (Factory Constructor)
5-1. 캐싱 — Logger
class Logger { final String name; static final Map<String, Logger> _cache = {}; Logger._internal(this.name); factory Logger(String name) { return _cache.putIfAbsent(name, () => Logger._internal(name)); } } final l1 = Logger('UI'); final l2 = Logger('UI'); print(identical(l1, l2)); // true5-2. 하위 클래스 반환 — Shape
abstract class Shape { factory Shape(String type) { switch (type) { case 'circle': return Circle(10); case 'rectangle': return Rectangle(10, 20); default: throw ArgumentError('지원하지 않는 도형: $type'); } } double get area; } class Circle implements Shape { final double radius; Circle(this.radius); @override double get area => 3.14 * radius * radius; } class Rectangle implements Shape { final double width, height; Rectangle(this.width, this.height); @override double get area => width * height; }5-3. fromJson — User
Flutter API 응답 모델에 필수 패턴.
class User { final String name; final int age; final String email; User(this.name, this.age, this.email); factory User.fromJson(Map<String, dynamic> json) { return User(json['name'] as String, json['age'] as int, json['email'] as String); } Map<String, dynamic> toJson() => {'name': name, 'age': age, 'email': email}; }6. 정적 멤버 (Static)
class MathUtils { static const double PI = 3.14159; static int calculationCount = 0; static double square(double num) { calculationCount++; return num * num; } } print(MathUtils.PI); print(MathUtils.square(5)); // 25.07. 추상 클래스
abstract class Animal { String name; Animal(this.name); void makeSound(); void sleep() { print('$name is sleeping'); } } class Dog extends Animal { Dog(String name) : super(name); @override void makeSound() { print('$name says Woof!'); } }8. 인터페이스 (implements)
Dart는 별도 interface 키워드 없이, 모든 클래스가 인터페이스. implements 시 모든 멤버 재정의 필수.
class Car implements Vehicle { @override void move() { print('Car is driving'); } @override void stop() { print('Car stopped'); } } Vehicle vehicle = Car(); vehicle.move(); // 다형성9. 상속 (extends)
class Student extends Person { String school; Student(String name, int age, this.school) : super(name, age); @override void introduce() { super.introduce(); print('저는 $school에 다니고 있습니다.'); } }10. Mixin (with)
깊은 상속 없이 기능 재사용. with로 다중 Mixin 적용.
mixin Swimming { void swim() { print('수영 중'); } } mixin Flying { void fly() { print('비행 중'); } } class Duck extends Animal with Swimming, Flying { Duck(String name) : super(name); } final duck = Duck('오리'); duck.swim(); duck.fly();mixin CanFly on Bird — 특정 클래스에서만 사용 가능하도록 제한
11. extends vs implements vs with
| 키워드 | 다중 | 구현 상속 | 용도 |
|---|---|---|---|
| extends | 단일 | O | is-a 상속 |
| implements | 다중 | X | 인터페이스 |
| with | 다중 | O | 기능 조합 |
12. Named Parameters
class Person { String name; int age; String? address; Person({required this.name, required this.age, this.address}); } final p = Person(name: '홍길동', age: 30);13. 연산자 오버로딩
class Vector { final double x, y; const Vector(this.x, this.y); Vector operator +(Vector o) => Vector(x + o.x, y + o.y); @override bool operator ==(Object o) { if (identical(this, o)) return true; return o is Vector && o.x == x && o.y == y; } @override int get hashCode => x.hashCode ^ y.hashCode; } print(Vector(1,2) + Vector(3,4)); // Vector(4,6) print(Vector(1,2) == Vector(1,2)); // true핵심 정리
- ✓ Private — _ 접두사, getter로 노출
- ✓ 생성자 5종 — 기본/이름/초기화리스트/상수/리다이렉팅
- ✓ 팩토리 — 캐싱, 하위클래스 반환, fromJson
- ✓ 상속 — extends/implements/with
- ✓ 연산자 오버로딩 — operator +/==
実装ステップ
基本コンストラクタ — Person(this.name, this.age)でフィールド自動初期化の簡潔な文法
ファクトリコンストラクタ — factoryキーワードでキャッシュ/シングルトン/fromJson/条件別サブクラス返却
Mixin — withキーワードで多重機能組合せ、class Duck extends Animal with Swimming, Flying
abstract class — 抽象メソッドを定義しサブクラスで実装強制、インターフェース役割も兼ねる
メリット
- ✓ Mixinで多重継承の利点を安全に活用可能
- ✓ ファクトリコンストラクタで柔軟なオブジェクト生成パターン実現
デメリット
- ✗ コンストラクタ5種類で初心者には選択が難しい可能性