프로젝트를 만들면서 어떻게 하면 코드를 좋게 짤까? 여러 생각이 들었다. 

그래서 1번. 남들이 만든 코드를 보고 배운다. 

2번. 기본 개념을 더 파고들자. 

 

그래서 이번 글은 dart 기본 개념 공부이다.


 

[무료 프로그래밍 강의] 1시간만에 끝내는 객체지향 프로그래밍 - YouTube

 

이번 포스팅은 영상 내용의 날먹이다. 

좋은 자료 만들어주신 '코드팩토리'님께 감사할 따름이며, 좋아요 구독 해드리자. 


1. 객체지향 프로그래밍 OOP (Object Oriented Programming)

클래스 키워드를 사용하여 프로그래밍 하는 것! 

클래스는 변수와 함수로 구성

이런 클래스들을 사용해서 인스턴스를 만들면 쉽게 아이돌을 구현할 수 있다. 

 

2. 생성자

클래스의 효율성을 비교하기 위해서 아래와 같이 예시를 만들었다. 

 

 

class Idol{
  String name = '블랙핑크';
  List <String> members = ['지수', '제니', '리사', '로제'];
  
  void sayHello(){
    print('안녕하세요 블랙핑크 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 지수,제니,리사,로제 입니다.');
  }
  
}

위처럼 블랙핑크라는 클래스 하나를 만들고,

void main() {
 Idol blackPink = Idol();
//  Idol blackPink = new Idol(); 
// dart 언어에서는 new 생성자 있는거 없는거 차이가 없다. 
 
 print(blackPink.name);
 print(blackPink.members);
 blackPink.sayHello();
 blackPink.introduce(); 
  
}

main 함수 안에서 blackPink 인스턴스를 만들었다 .

문제는 모든 인스턴스가 blackPink 똑같은 내용을 가진다. 

매개변수를 받아서 클래스를 다르게 사용할려면? 

constructor 생성자를 사용

 

클래스에서 매개변수를 받을수 있게 변경해보자

  
  Idol(String name, List<String> members)
    :this.name= name,
     this.members = members;

idol 클래스 안에 생성자를 넣는다 . 

  Idol(String name, List<String> members)
    :this.name= name,
     this.members = members;
  
  Idol(String name, List<String>members){
    this.name= name;
    this.members= members;
  }
  
  Idol(this.name, this.members);

물론 위에 생성자는 다 같은 뜻을 의미한다.

 

 

Idol 안에 매개변수를 받는다 .

여기서 this.name 에서 name은 idol 클래스의 name을 의미한다.

 

 

 

메서드에서도 매개변수 사용하기

class Idol{
  String name ;
  List <String> members ;
  
  Idol(String name, List<String> members)
    :this.name= name,
     this.members = members;
  
  
  void sayHello(){
    print('안녕하세요 ${this.name} 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 ${this.members} 입니다.');
  }
  
}

 

named Constructor 사용하기 

  Idol.fromList(List value)
    :this.name= value[0],
     this.members= value[1];

하나의 리스트로 매개변수를 받는다. 

물론 두가지 방법 같이 써도 된다. 

Immutable 프로그래밍?

요즘은 한번 값을 정하면 변하지 않는 immutable 프로그래밍을 많이 한다 .

만약 blackPink.name 이렇게 임의로 값을 변경하면 초기화된다.

fromList에서 초기한 값을 다시 초기화 시키는건 지양 해야한다. 

 

fianl 키워드를 사용한다 .

한번만 초기화 할 수 있도록! 

class Idol{
  final String name ;
  final List <String> members ;
  
//   Idol(String name, List<String> members)
//     :this.name= name,
//      this.members = members;
  
  Idol(this.name, this.members);
  
  Idol.fromList(List value)
    :this.name= value[0],
     this.members= value[1];

  void sayHello(){
    print('안녕하세요 ${this.name} 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 ${this.members} 입니다.');
  }
  
}

final은 한번 설정하면 바꿀수 없다. 

언제 선언이 될까? => 인스턴스 생성할 때!

'name' can't be used as a setter because it's final

name을 초기화 할 수 없다 ! final 로 선언했기 때문!

 

const 로 선언한다면? 

const 로 생성자를 만들거나, 그냥 생성자를 만들거나 별 차이는 없다. 

다른 점은 인스턴스를 만들때 const 로 만들 수 있다는 점! 

const Idol(this.name, this.members);

인스턴스를 const 로 만들 수 있다. 

중요한건 const는 빌드 타임 (컴파일 타임과 동의어 인지 잘모르겠습니다. ) 에 선언되기 때문에 값이 지정되 있어야한다.

만약 런 타임에 값이 정해지는 DateTime.now 같은 객체? 를 넣으면 에러가 뜬다.

arguments of a constant creation must be constant expressions.

상수 값 매개변수는 상수 표현이어야한다? 

만약 fromList 에 const 키워드를 넣으면?

Evaluation of this constant expression throws an exception.

상수 표현의 평가가 예외를 쓰로우 합니다. 

왜? 

찾아보니 array 는 빌드 타임에 메모리에 선언되고

list는 런 타임에 선언되어서 사용이 불가능하다. 

인스턴스 비교

위처럼 blackpink, blackpink2 이름으로 인스턴스를 생성해보자.

비교 해보면, 

blackpink와 blackpink2는 같습니까?: false

결과는 false 이다. 

인스턴스는 생성될 때마다 새로운 메모리에 올라간다. 

클래스는 참조형 데이터이기 때문에 값이 아니라 값을 가지고 있는 메모리 주소를 가르키기 때문

 

[Chapter 1 변수] 7. 참조형 변수(Reference Variable)의 기본개념 (tistory.com)

 

[Chapter 1 변수] 7. 참조형 변수(Reference Variable)의 기본개념

우리가 지금까지 변수에 대해서 알아보면서 중점적으로 건드렸던 것은 변수 중에서도 기본형 변수(Primitive Variable)였다. 그런데 앞서 변수의 타입에 대해서 알아볼 때 우리가 배우는 자바(Java)에

colossus-java-practice.tistory.com

여기 설명이 잘되어있다.

하지만 인스턴스를 똑같은 값으로 하고 const 로 선언했을때는 ?

blackpink와 blackpink2는 같습니까?: true가 뜬다.

매우 중요하다!  

만약 내용을 한글자라도 다르게 하면 false 가 뜬다. 

 

3. Getter and Setter 

getter

class Idol{
 String name ;
 List <String> members ;
  
 Idol(this.name, this.members);
  
 Idol.fromList(List value)
    :this.name= value[0],
     this.members= value[1];

  void sayHello(){
    print('안녕하세요 ${this.name} 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 ${this.members} 입니다.');
  }
  
  
  String get firstMember {
    return this.members[0];
  }
}

먼저 getter 를 만들어보자 .

데이터 타입, get, 메서드 명으로 

아래와 같이 간단하게 구현할 수 있다. 

setter 에서 값을 변경해야하기 때문에 final , const 키워드는 일단 다 지웠다. 

void main() {
 Idol blackPink = Idol(
 '블랙핑크',
 ['지수',' 제니', '리사', '로제'] 
 );
 
  
  print(blackPink.firstMember);

  
}

메서드이기 때문에 인스턴스명.firstMember 로 간단하게 출력할 수 있다. 

특이한건 메서드 인데 괄호를 사용하지 않는다는점. 

setter 

  String get firstMember {
    return this.members[0];
  }
  
  set firstMember(String name){
    this.members[0]= name;
  }

set, 메서드 이름, 매개변수 

매개변수는 하나만 들어간다. 

void main() {
 Idol blackPink = Idol(
 '블랙핑크',
 ['지수',' 제니', '리사', '로제'] 
 );
 
  
  print(blackPink.firstMember);
  
  blackPink.firstMember = '코코';
  print(blackPink.firstMember);
  
  
}

setter는 그냥 안에 대입 시키면 된다. 

firstmember는 코코가 된다. 

get 을 왜 쓸까 ?

  String getFirstMember(){
      return this.members[0];
  }

이렇게 메서드로 지정하고 return 하면 되는거 아닌가 ?

뭐가 다른가 ?

기능적으로 차이가 없다.

가볍게 쓸거면 getter 를 쓰고 뭐 변경하는 로직이 필요하면 메서드를 써라

 

원래 final 이면 런 타임에 선언후 변경이 되지 않는다. => setter 가 먹히지 않는다. 

하지만 list 타입은 final 이어도 최기화 후 setter 로 변경할 수 있다.

하지만 아예 members list 를 매개변수로 받아서 this.members를 초기화 할 수 는 없다. 

따라서 setter는 잘 안쓰인다 . => immutable 프로그래밍에 지양되는 행위다. 

4. private 변수 

dart에서 private 변수는 같은 dart 페이지인 경우 사용할 수 있다. 

다른 페이지에 선언된 private 값은 사용할 수 없다. 

그냥 변수 이름에 '_' d언더 스코어로 선언하면 된다.

해당 dart 파일에서 private 변수를 사용하고 싶다면 언더 스코어로 불러내면 된다. 

맴버, 메서드 다 private 기호를 사용할 수 있다. 

 

5. 상속 inheritance 

oop 의 꽃 상속을 잘해야한다. 

void main(){
  print('----Idol----');
  Idol apink = Idol(name: '에이핑크', memberCount: 5);
  
  apink.sayName();
  apink.sayMemberCount();
  
}

// 상속 - inheritance
//
// 상속을 받으면 부모 클래스의 모든 속성을
// 자식 클래스가 부여받는다 .

class Idol {
  
  String name;
  
  int memberCount;
  
  Idol({
    required this.name,
    required this.memberCount
  });
  
  void sayName(){
    print('저는 ${this.name}입니다.');
  }
  
  void sayMemberCount(){
    print('${this.name}은 ${this.memberCount}명의 맴버가 있습니다.');
  }
  
}

이렇게 idol 클래스와 인스턴스를 다시 선언하고

 

상속을 통해서 남자아이돌, 여자아이돌로 나눤보자 

idol 로부터 extends 해보자 .

일단 오류가 뜬다.

The superclass 'Idol' doesn't have a zero argument constructor. 

상위 클래스 idol 은 zero 매개변수 생성자가 없다 

곧 상속을 받으면 상위 클래스의 생성자도 똑같이 선언해줘야한다. 

 

위에서 했던 것처럼 생성자를 만들었는데 오류가 뜬다.

'memberCount' isn't a field in the enclosing class.

this.name 의 name 과  this.memberCount 의 memberCount 가 해당 필드안에서 존재하지 않는다 ? 

 

부모 클래스를 지칭하는건 this 키워드가 아니라 super 이다 .

super()는 곧 상위 클래스의 생성자를 의미한다. 

idol 생성자의 모습

idol named construtor를 이용했으니까 이 방법을 그대로 적용해서 (이름에 맞게) 생성자를 초기화해야한다. 

이렇게 생성자를 구성하면 된다.

자식 클래스 초기화 하기 

sayMembersCount는 부모 클래스에 있는 메서드이지만 상속을 했기 때문에 여기서도 사용할 수 있다.

자식 클래스에 맴버,메서드를 추가할 수 있지만 추가한 걸 부모 클래스로 올려보낼 수 없다.

sayMale 메서드를 추가해서 사용할 수 있지만 해당 메서드가 부모 클래스로 올려보내져 사용되는 일은 없다. 

 

타입 체크

부모 클래스는 오직 부모 클래스 이다.

자식 클래스는 부모 클래스 이다.

자식 클래스는 자식 클래스 이다.

 

자식 클래스는 부모,자식 클래스 둘 다 가능하다는 점! 

 

 


2부에 계속~ 

+ Recent posts