-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 연산자는 인스턴스에서 해당 인스턴스의 속성이나 멤버 함수를 연속해서 호출할 때 사용. |