본문 바로가기
프로그래밍 언어/Dart

[Dart] 추상화 클래스와 상속, Mixin

by BK0625 2023. 10. 25.
반응형

추상화 클래스

추상화 클래스는 객체를 생성할 수 없는 클래스이다. 추상화 클래스는 다른 클래스들이 직접 구현 해야 하는 메소드들을 모아놓은 것이라고 할 수 있다. 먼저 추상화 클래스를 작성해보자

 

abstract class Human {
  void walk(){} //반환 값과 함수 이름만 선언
}

 

Human이라는 추상화 클래스는 walk라는 메소드를 가지고 walk 메소드는 void 반환타입을 가진다. 그리고 이 Human 추상화 클래스를 사용하기 위해서 Player 클래스가 Human 클래스를 상속을 받는다. 다음과 같이 하면 된다.

 

class Player extends Human{
  String name; 
  int xp;
  Team team;
 
  
  Player({required this.name,required this.xp,required this.team});
  

  void sayHello(){
    print("hi my name is $name"); //클래스 함수 내에서는 this를 쓸 필요가 없음
  }
  
}

 

이렇게 되면 에러가 발생하는데 그 이유는 Human 클래스는 walk란 메소드가 있는데 Human을 상속받은 Player 클래스는 walk 메소드를 가지고 있지 않기 때문이다. 즉 추상화클래스는 반환타입, 함수 이름, 파라미터만 설정하고 그 추상화 클래스를 상속받는 클래스들이 상속, 확장 할 수 있는 것이다. 그럼 Player 클래스에 walk 메소드를 작성해주면 된다.

 

abstract class Human {
  void walk(){} //반환 값과 함수 이름만 선언
}

class Player extends Human{
  String name; 
  int xp;
  Team team;
 
  
  Player({required this.name,required this.xp,required this.team});
  

  void sayHello(){
    print("hi my name is $name"); //클래스 함수 내에서는 this를 쓸 필요가 없음
  }
  
  void walk(){
    print('walking...');
  }
  
}

 

만약 다른 클래스가 있다면

 

abstract class Human {
  void walk(){} //반환 값과 함수 이름만 선언
}

class Player extends Human{
  String name; 
  int xp;
  Team team;
 
  
  Player({required this.name,required this.xp,required this.team});
  

  void sayHello(){
    print("hi my name is $name"); //클래스 함수 내에서는 this를 쓸 필요가 없음
  }
  
  void walk(){
    print('walking...');
  }
  
}

class Coach extends Human{
  void walk(){
    print('hello walk');
  }

}

 

이렇게 구현이 가능하다. 즉 추상화 클래스는 특정 메소드를 구현하도록 강제하고 상속받는 모든 클래스가 가지고 있어야 하는 메소드를 정의하고 있다. 그러면 그 추상화 클래스를 상속 받은 클래스들이 어떤 메소드를 가져야 하는지 알 수 있다. 그리고 해당 클래스에 따라 다르게 구현(확장) 할 수 있다.

 

상속

상속은 워낙 많은 프로그래밍 언어에서 지원하는 기능이다. 간단히 말하자면 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미한다. 상속을 이용하면 기존에 정의되어 있던 클래스의 모든 필드와 메소드를 물려받게 된다. 기존 작성된 클래스를 재활용할 수 있고 자식 클래스 설계시 중복되는 프로퍼티를 미리 부모 클래스에 작성해 놓으면 자식 클래스에는 해당 멤버를 작성하지 않아도 되는 등의 장점이 있다. 먼저 Human 클래스를 작성해보자.

 

class Human {
  final String name;
  Human(this.name);
  void sayHello(){
    print("Hi my name is $name");
  }
}

 

그리고 Player 클래스를 작성할텐데 Player도 사람이고 이름이라는 값을 가질테니 Human 클래스를 상속해준다. 상속하는 방법은 다음과 같다.

 

 

이렇게 되면 Player 클래스는 Human 클래스의 모든 것을 다 상속 받게 된다. Player는 Team이라는 고유 프로퍼티를 가진다.

 

그리고 생성자를 만들텐데 이 때 Human 클래스의 생성자를 호출해야한다. name에 값을 넣어주어야 하기 때문이다. 다음과 같이 작성한다.

 

class Human {
  final String name;
  Human(this.name);
  void sayHello(){
    print("Hi my name is $name");
  }
}

enum Team { blue, red}

class Player extends Human{
  final Team team;
  
  Player({
    required this.team,
    required String name
  }) : super(name);
}

void main() {
  var player = Player(team: Team.red, name:'chobkyu');
  
}

 

Player 클래스는 main 메서드에서 인스턴스를 생성할 때 team고 name 변수를 받게 된다. 문제는 Human의 생성자에 값을 받아야 하기 때문에 받은 name 값을 Human 클래스에 전달해야 한다. 이 때  : 와 super 키워드를 사용해서 name을 Human 클래스에 전달한다. 즉 super는 확장을 한 부모 클래스와 상호 작용을 할 수 있게 해준다.

 

super 키워드를 통해 부모 클래스의 메소드도 확장이 가능하다. 만약 Human 클래스의 sayHello 메소드 뒤에 무언가 더 말을 하도록 변경하고 싶다면 다음과 같이 하면 된다.

 

class Human {
  final String name;
  Human(this.name);
  void sayHello(){
    print("Hi my name is $name");
  }
}

enum Team { blue, red}

class Player extends Human{
  final Team team;
  
  Player({
    required this.team,
    required String name
  }) : super(name);
  
  @override
  void sayHello(){
    super.sayHello();
    print('override!! my team is $team');
  }
}

 

 

먼저 @override로 원래 메소드를 직접 만든 메소드로 대체하고 원래 대로 void sayHello를 작성한다. 다만 먼저 Human 클래스의 sayHello를 호출하고 싶다면 super.sayHello를 통해 Human 클래스의 메서드를 가져와서 사용할 수 있다. 즉 부모 클래스의 프로퍼티에 접근하거나 메소드를 호출할 수 있다는 것이다.

 

Mixin

Mixin은 생성자가 없는 클래스를 의미한다. 다음과 같은 클래스들이 있다. 원래는 class 키워드로 생성이 가능했지만 이제는 mixin 키워드로 생성해야 한다.

 

mixin Strong {
  final double strengthLevel = 15.99;
}

mixin QuickRunner {
  void runQuick(){
    print('run!');
  }
}

 

이 때 내 player가 강하고 빠른 캐릭터이기리 원할 때 with 키워드를 사용해 QuickRunner와 Strong 클래스에 있는 프로퍼티와 메소드를 Player에 담아준다. 한마디로 꼭 상속을 하지 않고 다른 클래스의 프로퍼티와 메소드를 긁어오는 것이다.

 

class Player with Strong, QuickRunner{
  final Team team;
  Player({
    required this.team,
  });
}

 

이러한 Mixin 클래스들은 여러 클래스에 재사용이 가능하다는 것이다. 만약 Horse라는 클래스가 있다면 말 역시 빠르고 힘이 셀 테니 with 키워드를 사용해 재사용할 수 있을 것이다. 즉 상속과는 달리 Mixin 내부의 프로퍼티와 메소드들을 가져오는 것 뿐이다.

반응형

'프로그래밍 언어 > Dart' 카테고리의 다른 글

[Dart] Cascade Notation  (0) 2023.10.25
[Dart] 클래스 - 클래스와 생성자  (2) 2023.10.25
[Dart] 함수  (0) 2023.02.26
[Dart] 자료형  (0) 2023.02.22
[Dart] Dart를 시작해보자  (0) 2023.02.21