From d2b00fd979252c43d81ef5acc7c1aefdcd58a45b Mon Sep 17 00:00:00 2001 From: Roo Ryu <34956359+deepbig@users.noreply.github.com> Date: Mon, 22 Jul 2024 02:06:13 +0900 Subject: [PATCH] Create ch02.md --- .../ch02.md" | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 "hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch02.md" diff --git "a/hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch02.md" "b/hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch02.md" new file mode 100644 index 0000000..47fb853 --- /dev/null +++ "b/hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch02.md" @@ -0,0 +1,337 @@ +# 02. 다트 객체지향 프로그래밍 + +- 다트 언어는 높은 완성도로 객체지향 프로그래밍을 지원함. +- 플러터 역시 객체지향 프로그래밍 중심으로 설계된 프레임워크. + +### 2.1. 객체지향 프로그래밍의 필요성 + +- Map을 사용하면 단순히 값을 저장하는 것 외에 추가적인 편의기능을 구현할 수 없음. +- 클래스를 만들어 사용하면 필요한 값들만 입력하도록 제한하고 클래스에 특화된 하뭇들을 선언할 수 있음. +- 클래스는 일종의 설계또로서 데이터가 보유할 속성과 기능을 정의하는 자료구조. +- 클래스가 설계도, 인스터스화가 실물(실제 사용할 수 있는 데이터 생성). + +### 2.2. 클래스 + +- 객체지향 프로그래밍의 기본 = 클래스(class) +- 예제: + +```dart +class Idol { + String name = 'BANDIBOODI'; // 종속된 변수 = 멤버 변수, + + void sayName() { // 종속된 함수 = 메서드 + // 스코프 안에 값은 속성 이름이 하나만 존재한다면 this 생략 가능. + print('저는 ${this.name}입니다.'); + } +} + +void main() { + Idol bandiboodi = Idol(); + bandiboodi.sayName(); // 저는 BANDIBOODI입니다. +} +``` + +- 함수는 메서드를 포함하는 더 큰 개념임. +- 클래스에 정의된 함수인 메서드는 클래스의 기능을 정의한 함수 + +생성자 + +- constructor = 클래스의 인스턴스를 생성하는 메서드. + +```dart +class Idol { + final String name; + + Idol(String name) : this.name = name; +} + +// 생성자의 매개변수를 변수에 저장하는 과정을 생략할 수도 있음. +class Idol2 { + final String name; + + // this를 사용할 경우, 해당되는 변수에 자동으로 매개변수가 저장됨. + Idol(this.name); +} +``` + +- 생성자에서 입력받을 변수는 일반적으로 final로 선언함 (인스턴스화한 다음에 변수의 값을 변경하지 못하도록 하기 위해.) + +네임드 생성자 + +- named constructor = named parameter와 비슷한 개념. +- 일반적으로 클래스를 생성하는 여러 방법을 명시하고 싶을 때 사용. + +```dart +class Idol { + final String name; + final int membersCount; + + Idol(String name, int membersCount) + : this.name = name, + this.membersCount = membersCount' + + // 네임드 생성자 + Idol.fromMap(Map map) + : this.name = map['name'], + this.membersCount = map['membersCount']; +} +``` + +프라이빗 변수 + +- 다트에서의 private variable은 다른 언어와 정의가 조금 다름. +- 일반적으로 private variable은 class 내부에서만 사용하는 변수를 칭하지만, 다트 언어에서는 **같은 파일에서만 접근 가능한 변수임.** + +```dart +class Idol { + // _ 로 변수명을 시작하면 private variable을 선언할 수 있음. + String _name; + + Idol(this._name); +} + +void main() { + Idol bandiboodi = Idol('BANDIBOODI'); + + // 같은 파일에서는 _name 변수에 접근할 수 있지만, + // 다른 파일에서는 _name 변수에 접근할 수 없음. + print(bandiboodi._name); // BANDIBOODI +} +``` + +Getter / Setter + +- 최근에는 객체지향 프로그래밍을 사용할 때 변수의 값에 불변성(immutable: 인스턴스화 후 변경할 수 없는)을 특성으로 사용하기 때문에 Setter는 거의 사용하지 않음. + +```dart +class Idol { + String _name = 'BANDIBOODI'; + + String get name { + return this._name; + } + + // Setter는 매개변수로 딱 하나의 변수를 받을 수 있음. + set name(String name) { + this._name = name; + } +} + +void main() { + Idol bandiboodi = Idol(); + + bandiboodi.name = 'test'; // setter 사용. + print(bandiboodi.name); // getter 사용. 결과값: test +} +``` + +### 2.3. 상속 + +- extends 키워드를 사용해 상속(inheritance)할 수 있음. +- 상속은 어떤 클래스의 기능을 다늘 클래스가 사용할 수 있게 하는 기법. + +```dart +class BoyGroup extends Idol { + // 상속받은 생성자 + BoyGroup( + String name, + int membersCount, + ) : super( + name, + memberCount, + ); + +} +``` + +- super는 상속한 부모 클래스를 지칭 +- 부모 클래스에 기본 생성자가 있기때문에 BoyGroup에서는 Idol 클래스의 생성자를 실행하는 구조. + +### 2.4. 오버라이드 + +- override = 부모 클래스 또는 interface에 정의된 메서드를 재정의할 때 사용됨. +- 다트에서는 override 키워드를 생략할 수 있기 때문에 override 키워드를 사용하지 않고도 메서드를 재정의할 수 있음. + +```dart +class GirlGroup extends Idol { + // 생성자의 매개변수로 직접 super 키워드를 사용할 수도 있음. + GirlGroup( + super.name, + super. membersCount, + ); + + @override + void sayName() { + print('저는 여자 아이돌 ${this.name}입니다.'); + } +} +``` + +- 한 클래스에 이름이 값은 메서드가 존재할 수 없기 때문에 부모 클래스나 인터페이스에 이미 존재하는 메서드명을 입력하면 override 키워드를 생략해도 메서드가 덮어써짐. +- 하지만 직접 명시하는 게 협업 및 유지보수에 유리함. + +### 2.5. 인터페이스 + +- 상속은 공유되는 기능을 이어받는 개념. +- interface = 공통으로 필요한 기능을 정의만 해두는 역할. +- 다트에는 interface를 지정하는 키워드가 따로 없음. +- 상속은 단하나의 클래스만 할 수 있지만 인터페이스는 적용 개수에 제한이 없음. + +```dart +class GirlGroup implements Idol { + final String name; + final int membersCount; + + GirlGroup( + this.name, + this.membersCount, + ); + + void sayName() { + // ... + } + + void sayMembersCount() { + // ... + } +} +``` + +- 반드시 재정의할 필요가 있는 기능을 정의하는 용도로 인터페이스 사용. 실수로 빼먹는 일을 방지. + +### 2.6. 믹스인 + +- mixin = 특정 클래스에 원하는 기능들만 골라 넣을 수 있는 기능. +- 특정 클래스를 지정해서 속성들을 정의할 수 있으며, 지정한 클래스를 상속하는 클래스에서도 사용할 수 있음. +- 한 개의 클래스에 여러 개의 믹스인을 적용할 수도 있음. + +```dart +mixin IdolSingMixin on Idol { + void sing() { + print('${this.name}가 노래르 부른다.'); + } +} + +// mixin을 적용할 때는 with 키워드 사용 +class BoyGroup extends Idol with IdolSingMixin { + BoyGroup( + super.name, + super.membersCount, + ); +} + +void main() { + BoyGroup bandi = BoyGroup('BANDI', 6); + + // 믹스인에 정의된 sing() 함수 사용 가능 + bandi.sing(); // BANDI가 노래를 부른다. +} +``` + +### 2.7. 추상 + +- abstract = 상속이나 인터페이스로 사용하는 데 필요한 속성만 정의하고 인스턴스화할 수 없도록 하는 기능. +- 클래스를 인터페이스화할 일이 없다면, 클래스를 추상 클래스로 선언해서 해당 클래스의 인스턴스화를 방지하고 메서드 정의를 자식 클래스에 위임하는 방식. +- 추상 크랠스는 추상 메서드를 선언. +- 추상 메서드는 함수의 반환 타입, 이름, 매개변수만 정의. + +```dart +abstract class Idol { + final String name; + final int membersCount; + + Idol(this.name, this.membersCount); + + void sayName(); + void sayMembersCount(); +} +``` + +### 2.8. 제네릭 + +- generic = 객체지향 프로그래밍에서 가장 아름다운(?) 기능. +- 제네릭은 클래스나 함수의 정의 → 클래스를 선언할 때 X, 인스턴스화하거나 실행할 때로 미룬다. +- 특정 변수의 타입을 하나의 타입으로 제한하고 싶지 않을 때 자주 사용. +- 예를 들어, 정수를 받는 함수, 문자열을 받는 함수를 각각 `setInt()`, `setString()` 처럼 따로 만들지 않아도, 제네릭을 사용해 `set()` 함수 하나로 여러 자료형을 입력받게 처리할 수 있음. +- Map, List, Set 등에서 사용한 `<>` 사이에 입력되는 값이 제네릭 문자임. + +```dart +// 인스턴스화할 때 입력받을 타입을 T로 정의함. +class Cache { + // data의 타입을 추후 입력될 T 타입으로 지정함. + final T data; + + Cache({ + required this.data, + }); +} + +void main() { + // T의 타입을 List로 입력함. + final cache = Cache>( + data: [1,2,3], + ); + + // 제네릭에 입력된 값을 통해 data 변수의 타입이 자동으로 유추됨. + print(cache.data.reduce((value, element) => value + element)); // 6 +} +``` + +- 일반적으로 개발자들이 사용하는 제네릭 문자들 + - T: 변수 타입 표현 + - E: 리스트 내부 요소들의 타입을 표현 + - K: 키를 표헌 + - V: 값을 표현 + +### 2.9. Static + +- static = 클래스의 인스턴스에 귀속되지 않고, 클래스 자체에 귀속됨. + +```dart +class Counter { + static int i = 0; + + Counter() { + print(i++); + } +} + +void main() { + Counter(); // 1 + Counter(); // 2 + Counter(); // 3 +} +``` + +- 정적 변수 i는 Counter class에 귀속되기 때문에 instance를 호출할 때마다 1 씩 증가됨. +- 따라서, static 키워드는 인스턴스끼리 공유해야하는 정보에 지정. + +### 2.10. cascade operator + +- cascade operator = 인스턴스에서 해당 인스턴스의 속성이나 맴버 함수를 연속해서 사용하는 기능. +- 사용 기호는 `..` , 장점은 더 간결한 코드 작성 가능. + +```dart +void main() { + // cascade operator를 사용하면 선선한 변수의 메서드를 연속으로 실행 가능. + Idol bandiboodi = Idol('BANDIBOODI', 13) + ..sayName() // 저는 BANDIBOODI입니다. + ..sayMembersCount(); // BANDIBOODI 멤버는 13명입니다. +} +``` + +### 핵심 요약: + +1. Class 키워드를 사용해서 클래스를 선언할 수 있음. +2. 클래스를 인스턴스화하면 클래스의 인스턴스를 변수로 지정할 수 있음. +3. 상속받으면 부모 클래스의 모든 속성을 물려받음. + 1. extends 키워드 사용 + 2. 하나의 자식 클래스는 하나의 부모 클래스만 상속 받을 수 있음. +4. Override는 이미 선언되어 있는 속성을 덮어쓰는 기능. +5. Interfac는 클래스의 필수 속성들을 정의하고 강제할 수 있는 기능. +6. Mixin은 상속처럼 모든 속성을 물려받지 않고 원하는 기능만 골라서 적용할 수 있음. + 1. with 키워드를 사용해서 믹스인을 적용 + 2. 하나의 클래스에 여러 개의 믹스인을 적용할 수 있음. +7. Generic은 변수 타입의 정의를 인스턴스화까지 미룰 수 있음. +8. Static은 클래스에 직접 귀속되는 속성. +9. Cascade 연산자는 인스턴스에서 해당 인스턴스의 속성이나 멤버 함수를 연속해서 호출할 때 사용.