2단계 다트 객체지향 프로그래밍

송기영·2023년 11월 27일
0

플러터

목록 보기
4/25

2. 객체지향 프로그래밍의 필요성

main() 함수에서 모든 코드를 작성하면 코드 정리가 안 돼 유지보수 및 협업에 큰 장애물이 된다.

클래스를 사용해서 서로 밀접한 관계가 있는 함수와 변수를 묶어두면 코드 관리가 용이하다.

아파트를 지을때 설계도만 있다고 아파트가 지어지지 않는다. 실제로 아파트를 지어야 실물 아파트가 생긴다. 즉 설계도 - 클래스, 실물 아파트 - 인스턴스화로 정의할 수 있다.

2.1. 클래스

class Idol {
	String name = "블랙핑크";

	void sayName() {
		// 클래스 내부 속성을 지칭할때는 this키워드를 붙인다.
		print(this.name);  // 블랙핑크
		// 스코프 안에 같은 속성이 하나만 존재한다면 this 생략이 가능하다.
		print(name);  // 블랙핑크
	}
}

void main(){
	Idol blackPink = Idol();
	blackPink.sayName();
}

💡Tips: Dart 2.0 이후에는 new키워드를 붙이지 않도 인스턴스를 생성 가능하다고 한다.

2.3.1. 생성자

class Idol {
	final String name; // 생성자에서 입력받는 변수들은 일반적으로 final 키워드 사용
	
	Idol(String name) : this.name = name; // 생성자 선언 
	Idole(this.name);                     // 생성자 선언과 동일함
	
	void sayName() {
		// 클래스 내부 속성을 지칭할때는 this키워드를 붙인다.
		print(this.name);  // 블랙핑크
		// 스코프 안에 같은 속성이 하나만 존재한다면 this 생략이 가능하다.
		print(name);  // 블랙핑크
	}
}

void main(){
	Idol blackPink = Idol("블랙핑크");
	blackPink.sayName();
}

💡Tips: 생성자의 매개변수를 final로 선언하는 것은 불변성, 코드안정성, 초기화 보장을 위해서 이며 만약에 매개변수 매개변수 값이 함수내에서 바뀌어야 하는 경우에는 final을 생략한다.

2.3.2. 네임드 생성자

네임드 파라미터와 상당히 비슷한 개념으로 클래스를 생성하는 여러 방법을 명시하고 싶을 때 사용한다.

class Idol {
	final String name;
	final int memberCount;
	
	// 생성자
	Idol(this.name, this.memberCount);

	// 네임드 생성자
	Idol.formMap(<Map<String, dynamic> map)
			: this.name = map["name"],
				this.memberCount = map["memberCount"],
}

void main() {
	Idol blackPink = Idol("블랙핑크", 4);
	Idol bts = Idol.fromMap({"name": "BTS", "memberCount": 7});
}

2.3.3. 프라이빗 변수

같은 파일에서만 접근이 가능한 변수이다. _로 시작하면 프라이빗 변수를 선언할 수 있다.

class Idol {
	String _name;
	Idole(this._name);
}

2.3.4. 게터/세터

게터는 값을 가져올 때 사용되고, 세터는 값을 지정할 때 사용된다. 최근에는 변수의 값을 불변성 특성으로 사용하기 때문에 세터는 거의 사용하지 않는다.

class Idol {
	String _name = "블랙핑크";

	String get name {
		return this._name;
	}

	set name(String name) {
		this._name = name;
	}
}

void main() {
	Idol blackPink = Idol();
	blackPink.name = "에이핑크"; // setter
	print(blackPink.name); // getter
}

2.3.5. 상속

어떤 클래스의 기능을 다른 클래스가 사용할 수 있게하는 기법, 모든 기능을 재정의할 필요가 없다.

class Idol {
	final String name;
	final int membersCount;

	Idol(this.name, this.membersCount);
	
	void sayName() {
		print("저는 ${this.name} 입니다.");
	}

	void sayMembersCount(){
		print("${this.name}멤버는 ${this.membersCount}명입니다.");
	}
}

class BoyGroup extends Idol {
	BoyGroup(String name, int membersCount)
		: super(name, membersCount);  // super는 부모 클래스를 지칭

	void sayMale() {
		print("저는 남자 아이돌입니다.");
	}
}

void main() {
	BoyGroup bts = BoyGroup("BTS", 7);
  bts.sayMale();
  bts.sayName();
  bts.sayMembersCount();
}

2.3.6. 오버라이드(메서드 재정의)

부모 클래스 또는 인터페이스에 정의된 메서드를 재정의할 때 사용된다.

class Idol {
	final String name;
	final int membersCount;

	Idol(this.name, this.membersCount);
	
	void sayName() {
		print("저는 ${this.name} 입니다.");
	}

	void sayMembersCount(){
		print("${this.name}멤버는 ${this.membersCount}명입니다.");
	}
}

class GirlGroup extends Idol {
	GirlGroup(super.name, super.membersCount);

	// 생략가능
	
	void sayName() {
		print("저는 여자아이돌 ${this.name}입니다.");
	}
}

void main() {
	GirlGroup test = GirlGroup("카이", 10);
  test.sayName();
}

2.4. 인터페이스

공통으로 필요한 기능을 정의만 해두는 역할을 한다. 반드시 모든 기능을 다시 정의 해주어야 한다.

실수로 빼먹는 일을 방지한다.

class GrilGoup implements Idol {
	final String name;
	final int membersCount;

	GrilGroup(this.name, this.membersCount);
	
	void sayName() {
		print("저는 여자 아이톨 ${this.name}입니다. ");
	}

	void sayMembersCount() {
		print("${this.name} 멤버는 ${this.membersCount}명입니다.");
	}
}

2.5. 믹스인(mixin)

특정 클래스에 원하는 기능들만 골라넣을 수 있는 기능이다. 특정 클래스를 지정해서 속성들을 정의할 수 있으며 지정한 클래스를 상속하는 클래스에서도 사용할 수 있다.

인터페이스처럼 한 개의 클래스에 여러 개의 믹스인을 적용할 수도 있다.

다트는 다중상속을 지원하지 않기때문에 믹스인을 사용해서 다중상속을 처리할 수 있다.

class Idol {
	final String name;
	final int membersCount;

	Idol(this.name, this.membersCount);
	
	void sayName() {
		print("저는 ${this.name} 입니다.");
	}

	void sayMembersCount(){
		print("${this.name}멤버는 ${this.membersCount}명입니다.");
	}
}

mixin IdolSingMixin on Idol {
	void sing() {
		print("${this.name}이 노래를 부릅니다.");
	}
}

class BoyGroup extends Idol with IdolSingMixin{
	BoyGroup(super.name, super.membersCount);
	void sayMale() {
		print("저는 남자 아이돌입니다.");
	}
}

mixin LoggingMixin {
  void log(String message) {
    print('Log: $message');
  }
}

// 클래스에 mixin 적용
class MyClass with LoggingMixin {
  void doSomething() {
    log('Doing something');
  }
}

void main() {
	BoyGroup bts = BoyGroup("BTS", 7);
	bts.sing();
  var myObject = MyClass();
  myObject.doSomething();
}

2.5.1. 다중상속 대체

mixin Singer {
	void sing() {
		print("Singing");
	}
}

mixin Dancer {
	void dance(){
		print("Dancing");
	}
}

class Performer with Dancer, Singer {
	void perform(){
		print("Perform");
	}
}

void main(){
	Performer performer = Performer();
	performer.sing();
	performer.dance();
	performer.perform();
}

2.6. 추상

상속이나 인터페이스로 사용하는데 필요한 속성만 정의하고 인스턴스화 할 수 없도록 하는 기능이다. 상속을 받은 클래스가 추상 클래스의 모든 기능을 정의하지 않으면 컴파일 오류가 발생한다.

abstract class Idol {
	final String name;
	final int membersCount;
	
	Idol(this.name, this.membersCount);

	void sayName();
	void sayMembersCount();
}

// GirlGroup클래스는 Idol 클래스의 모든 기능을 정의해야한다.
class GirlGroup implements Idol {
	final String name;
	final int membersCount;
	
	Idol(this.name, this.membersCount);
	
	void sayName(){
		print("아이돌!${this.name}");
	}

	void sayMembersCount(){
		print("${this.membersCount)}명");
	}
}

2.7. 제네릭

클래스나 함수의 정의를 선언할 때가 아니라 인스턴스화하거나 실행할 때로 미룬다. 특정 변수의 타입을 하나의 타입으로 제한하고 싶지 않을 때 자주 사용한다. 여러 장료형을 입력받게 처리할 수 있다.

class Cache<T> {
	final T data;

	Cache({
		required this.data;
	});
}

void main() {
	final cache = Cache<List<int>>(
		data: [1,2,3],
	);

	print(cache.data.reduce((value, element) => value + element)); // 6
}

흔히 사용되는 제네릭 문자들

문자설명
T변수 타입을 표현할 때 흔히 사용된다. T value;
E리스트 내부 요소들의 타입을 표현할 때 흔히 사용된다. List
K키를 표현할 때 흔히 사용된다. Map<K, V>;
V값을 표현할 때 흔히 사용된다. Map<K, V>;

2.8. 스태틱

모든 속성은 각 클래스의 인스턴스에 귀속되지만 static을 사용하면 클래스 자체에 귀속된다.

인스턴스끼리 공유해야하는 정보에 지정하면 된다.

class Counter {
	static int i = 0;
	Counter(){
		i++;
		print(i);
	}
}

void main() {
	Counter counter = Counter();
	Counter counter1 = Counter();
	Counter counter2 = Counter();
}

2.9. 캐스케이드 연산자

인스턴스에서 해당 인스턴스의 속성이나 멤버 함수를 연속해서 사용하는 기능이다.

class Idol {
	final String name;
	final int membersCount;

	Idol(this.name, this.membersCount);
	
	void sayName() {
		print("저는 ${this.name} 입니다.");
	}

	void sayMembersCount(){
		print("${this.name}멤버는 ${this.membersCount}명입니다.");
	}
}

void main() {
  Idol blackPink = Idol("블랙핑크", 4)..sayName()..sayMembersCount();	
}

항상 JS/TS + 리액트를 이용해서 프론트 개발만하다가 객체지향적으로 공부를 해보니 문득 C#을 했던 기억이 떠올랐다.. 잘 지내니 내 첫회사

profile
업무하면서 쌓인 노하우를 정리하는 블로그🚀 풀스택 개발자를 지향하고 있습니다👻

0개의 댓글