Skip to content

Commit

Permalink
Create ch02.md
Browse files Browse the repository at this point in the history
  • Loading branch information
deepbig authored Jul 21, 2024
1 parent 92e2180 commit d2b00fd
Showing 1 changed file with 337 additions and 0 deletions.
337 changes: 337 additions & 0 deletions hong/flutter/코드팩도리의 플러터 프로그래밍/ch02.md
Original file line number Diff line number Diff line change
@@ -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<String, dynamic> 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<T> {
// data의 타입을 추후 입력될 T 타입으로 지정함.
final T data;
Cache({
required this.data,
});
}
void main() {
// T의 타입을 List<int>로 입력함.
final cache = Cache<List<int>>(
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 연산자는 인스턴스에서 해당 인스턴스의 속성이나 멤버 함수를 연속해서 호출할 때 사용.

0 comments on commit d2b00fd

Please sign in to comment.