드디어..!!
OOP의 꽃, class까지 왔다!
이번 프로젝트의 목표중 하나는
객체지향 프로그래밍의 장점을 최대한
활용하여, 어떤 팀원이 합류하더라도
내 의도에 맞게 코딩해 나갈수 있도록
규격을 정하는 것이다.
그러기 위해서는 내가 먼저 dart의
class 활용에 빠삭해야 한다.
여기에서 만큼은
dart 개발자문서를 열심히 보면서
유용한 기능들을 최대한 정리해보겠다.
Class
class Cafe {
}
void main() {
Cafe cafe1 = Cafe();
Cafe cafe2 = Cafe();
Cafe cafe3 = Cafe();
}
아무런 설정없이도 쓸 수 있다.
java같은 기본 세팅이 필요하지는 않은듯.
하지만 이렇게 쓸일은 없으니
constructor와 method를 만들어보자.
class Cafe {
String name = "스타벅스 신촌연세로점";
double latitude = 37.1234;
double longitude = 127.1234;
void introduce() {
print("이 카페의 이름은 ${name}이며, 위도 ${latitude}, 경도 ${longitude}에 위치해 있습니다.");
}
bool isStarbucks() {
return name.contains("스타벅스");
}
}
void main() {
Cafe cafe1 = Cafe();
print(cafe1.name); // 스타벅스 신촌연세로점
print(cafe1.latitude); // 37.1234
print(cafe1.longitude); // 127.1234
cafe1.introduce(); // 이 카페의 이름은 스타벅스 신촌연세로점이며, 위도 37.1234, 경도 127.1234에 위치해 있습니다.
print(cafe1.isStarbucks()); // true
}
oop개발에 익숙하다면 그냥 무난하구나 싶을거다.
한가지 특징은 클래스 옆에 소괄호 () 형태로
contructor를 지정해줄 수 있었던 코틀린과 달리
다트는 증괄호 {} 내부에만 지정해줄 수 있었다.
이제 constructor를 선언시에 받아서 설정해보자.
class Cafe {
String name;
double latitude;
double longitude;
Cafe(this.name, this.latitude, this.longitude);
void introduce() {
print("이 카페의 이름은 $name이며, 위도 $latitude, 경도 $longitude에 위치해 있습니다.");
}
bool isStarbucks() {
return name.contains("스타벅스");
}
}
void main() {
Cafe cafe1 = Cafe("스타벅스 신촌 연세로점", 37.1234, 127.1234);
Cafe cafe2 = Cafe("투썸플레이스 신촌점", 37.4321, 127.4321);
print(cafe1.name); // 스타벅스 신촌연세로점
print(cafe1.latitude); // 37.1234
print(cafe1.longitude); // 127.1234
print(cafe2.name); // 투썸플레이스 신촌점
print(cafe2.latitude); // 37.4321
print(cafe2.longitude); // 127.4321
cafe1.introduce(); // 이 카페의 이름은 스타벅스 신촌연세로점이며, 위도 37.1234, 경도 127.1234에 위치해 있습니다.
cafe2.introduce(); // 이 카페의 이름은 투썸플레이스 신촌점이며, 위도 37.4321, 경도 127.4321에 위치해 있습니다.
print(cafe1.isStarbucks()); // true
print(cafe2.isStarbucks()); // false
}
클래스 내부에서는 자신을 this로 받아 쓰는데,
내 경우에는 꼭 구분이 필요하지 않다면
굳이 붙이지 않는다.
함수때처럼 Named, Positional parameter와
default값들을 설정해줄 수 있다.
class Cafe {
String name;
double latitude;
double longitude;
Cafe({required this.name, this.latitude = 37.0, this.longitude = 127.0});
void introduce() {
print("이 카페의 이름은 $name이며, 위도 $latitude, 경도 $longitude에 위치해 있습니다.");
}
bool isStarbucks() {
return name.contains("스타벅스");
}
}
void main() {
Cafe cafe1 = Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
print(cafe1.name); // 스타벅스 신촌연세로점
print(cafe1.latitude); // 37.1234
print(cafe1.longitude); // 127
cafe1.introduce(); // 이 카페의 이름은 스타벅스 신촌연세로점이며, 위도 37.1234, 경도 127에 위치해 있습니다.
print(cafe1.isStarbucks()); // true
}
Named parametor야 자주 쓴다쳐도
default값은 굳이 쓸까 싶다.
(constructor 선언시에 써줄수 있으니까)
constructor 선언시에 위처럼
String, double등으로 선언해버리면
외부에서 접근해 값을 변경하는게 가능하다.
따라서 의도치 않은 버그를 예방하려면
final을 붙여주는 버릇을 들이는게 좋다.
class Cafe {
final String name;
double latitude;
double longitude;
Cafe({required this.name, this.latitude = 37.0, this.longitude = 127.0});
}
void main() {
Cafe cafe1 = Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
cafe1.latitude = 127.1122;
print(cafe1.latitude); // 127.1122
cafe1.name = "이디야 신촌점"; // error - 빨간줄
}
또, constructor를 받아올때
const를 붙이면 좋을 때가 있다고 한다.
class Cafe {
final String name;
final double latitude;
final double longitude;
const Cafe({required this.name, this.latitude = 37.0, this.longitude = 127.0});
}
void main() {
Cafe cafe1 = const Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
Cafe cafe2 = const Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
Cafe cafe3 = Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
Cafe cafe4 = Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
print(cafe1 == cafe2); // true
print(cafe3 == cafe4); // false
}
우리가 보기에 cafe1과 cafe2,
cafe3과 cafe4 모두 같아보이지만,
실제로는 다른 hash값을 가져
다른 객체로 인식된다.
그러나 클래스 내부에서 const constructor를 쓰고
선언할때에도 const로 선언해주면
두 객체의 동일 여부를 파악할 수 있게된다.
swift에서 equatable protocol을 걸어
두 객체간 동일성 여부를 지정해주는 것을
생각하면 이해하기 쉬울 것이다.
class를 private하게 만들 수 있다.
자바와 코틀린에서 private을
직접 붙여준것에 반해
다트에서는 언더바 _ 를 통해 만든다.
class _Cafe {
final String name;
final double latitude;
final double longitude;
const _Cafe({required this.name, this.latitude = 37.0, this.longitude = 127.0});
}
void main() {
_Cafe cafe1 = const _Cafe(name: "스타벅스 신촌 연세로점", latitude: 37.1234);
print(cafe1.name);
}
private 클래스는 기본적으로
해당 파일의 범위 내에서만 사용 가능하다.
(import해 사용 불가)
이는 class뿐만아니라 변수, 함수 모두에
적용 가능하다. (_만 앞에 붙이면 가능)
모든 oop 언어가 그렇듯 상속 개념이 있다.
extends를 사용하며, 기본적으로
한 클래스는 한 개의 클래스만
상속 받을 수 있다.
class Cafe {
final String name;
final double latitude;
final double longitude;
const Cafe({required this.name, required this.latitude, required this.longitude});
void introduce() {
print("이 카페의 이름은 ${name}이고, 위도는 ${latitude}, 경도는 ${longitude} 입니다.");
}
bool isStarbucks() {
return name.contains("스타벅스");
}
}
class BrandCafe extends Cafe {
final String brand;
BrandCafe({
required super.name,
required super.latitude,
required super.longitude,
required this.brand
});
bool isBig3Brand() {
return brand == "스타벅스" || brand == "메가커피" || brand == "컴포즈커피";
}
}
void main() {
BrandCafe cafe = BrandCafe(
name: "스타벅스 인천가좌점",
latitude: 37.1234,
longitude: 127.1234,
brand: "스타벅스"
);
cafe.introduce(); // 이 카페의 이름은 스타벅스 인천가좌점이고, 위도는 37.1234, 경도는 127.1234 입니다.
print(cafe.brand); // 스타벅스
print(cafe.isStarbucks()); // true
print(cafe.isBig3Brand()); // true
print(cafe is Cafe); // true
print(cafe is BrandCafe); // true
}
이와 같이 사용한다.
코드가 기니 포인트만 하나씩 짚어보자.
dart에서의 override는 Annotation을 통해
표시하여 활용할 수 있다. 바로 코드를 보자.
class Cafe {
final String name;
final double latitude;
final double longitude;
const Cafe({required this.name, required this.latitude, required this.longitude});
void introduce() {
print("이 카페의 이름은 ${name}이고, 위도는 ${latitude}, 경도는 ${longitude} 입니다.");
}
}
class BrandCafe extends Cafe {
final String brand;
BrandCafe({
required super.name,
required super.latitude,
required super.longitude,
required this.brand
});
@override
void introduce() {
super.introduce();
print("이 카페의 브랜드는 ${brand}입니다.");
}
}
void main() {
BrandCafe cafe = BrandCafe(
name: "투썸플레이스 인천가좌점",
latitude: 37.1234,
longitude: 127.1234,
brand: "투썸플레이스"
);
cafe.introduce();
}
// 이 카페의 이름은 투썸플레이스 인천가좌점이고, 위도는 37.1234, 경도는 127.1234 입니다.
// 이 카페의 브랜드는 투썸플레이스입니다.
override를 수행한 클래스에서
super를 통해 부모 클래스의 메소드를
수행할수도 있고, 완전히 새롭게
재구성할 수도 있다.
다음이 마지막 일듯?
여기서 class의 기본 활용을 다뤘으니
뒤에서 class 고급 활용을 정리한 뒤
바로 프로젝트로 뛰어들어보려 한다.