Extension
Extension은 기존 클래스에 새로운 메서드나 속성을 추가하는 기능입니다. Dart 2.7에서 도입된 이 기능은 기존 클래스의 소스를 수정하지 않고도 그 클래스에 메서드나 속성을 추가할 수 있게 해줍니다. 이는 외부 라이브러리나 시스템 클래스를 확장해야 할 때 유용하며, 특히 라이브러리나 SDK의 코드를 직접 수정할 수 없는 상황에서 매우 효과적입니다.
Extension 정의 및 사용법
- Extension은
extension
키워드를 사용하여 정의하며, 해당 클래스를 확장하는 형식을 가집니다. - 확장할 클래스 또는 타입을
on
키워드 뒤에 명시하여 그 타입에만 적용되는 메서드를 추가할 수 있습니다. - 다음은
List<int>
타입에 평균을 계산하는 메서드를 추가하는 예시입니다:
extension ListExtensions on List<int> {
double get average => this.isEmpty ? 0 : this.reduce((a, b) => a + b) / this.length;
}
void main() {
List<int> scores = [10, 20, 30];
print(scores.average); // 출력: 20.0
}
Extension의 장점
- 기존 코드 수정 없이 기능 추가: 클래스나 라이브러리를 확장할 수 있으며, 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있습니다.
- 가독성 향상: 확장된 메서드를 통해 코드가 더욱 직관적이고 명확하게 읽힐 수 있습니다.
- 코드 재사용성 증가: 특정 타입에 특화된 유틸리티 메서드를 정의하여 재사용성을 극대화할 수 있습니다.
Extension의 제한점
- 상태 유지 불가: Extension은 상태를 가질 수 없으며, 단지 메서드나 속성을 추가하는 데 그칩니다. 상태가 필요한 경우에는 Mixin이 더 적합할 수 있습니다.
- 다중 확장에 대한 주의: 여러 Extension이 동일한 메서드 이름을 사용할 경우 충돌이 발생할 수 있으므로, 확장 메서드의 이름을 고유하게 지정하는 것이 중요합니다.
Mixin
Mixin은 여러 클래스 간에 공통된 기능을 재사용할 수 있도록 해주는 기능입니다. Dart에서 Mixin은 상속 대신 사용할 수 있는 방법으로, 서로 다른 클래스에 공통 기능을 포함시킬 때 매우 유용합니다. Mixin은 클래스 간의 다중 상속 문제를 해결할 수 있으며, 코드 중복을 줄이고 더 유연한 설계를 가능하게 합니다. Dart에서 Mixin을 정의하려면 mixin
키워드를 사용합니다.
Mixin 사용법
with
키워드를 사용하여 Mixin을 클래스에 적용할 수 있으며, 다중 Mixin을 사용할 수도 있습니다.
mixin Swimming {
void swim() {
print('Swimming...');
}
}
mixin Flying {
void fly() {
print('Flying...');
}
}
class Duck with Swimming, Flying {
// Duck has access to swim() and fly() methods
}
class Fish with Swimming {
// Fish has access to swim() method
}
void main() {
Duck duck = Duck();
duck.swim(); // Output: Swimming...
duck.fly(); // Output: Flying...
Fish fish = Fish();
fish.swim(); // Output: Swimming...
Mixin 타입 제한
Mixin을 사용할 수 있는 타입을 제한할 수도 있습니다. 예를 들어, mixin이 정의하지 않은 메서드를 호출할 수 있는지에 따라 달라질 수 있습니다. 다음 예제처럼 on
키워드로 사용할 수 있는 부모 클래스를 제한함으로써 mixin의 사용을 제한할 수 있습니다.
class Musician {
// ...
}
mixin MusicalPerformer on Musician {
// ...
}
class SingerDancer extends Musician with MusicalPerformer {
// ...
}
Mixin의 제한점
- 복잡성 증가: 여러 개의 Mixin을 사용할 경우 클래스의 구조가 복잡해질 수 있습니다. Mixin이 여러 개 쌓일수록 클래스 간의 관계를 이해하기 어려울 수 있습니다.
- 상속과의 차이: 상속과 Mixin을 함께 사용할 경우 클래스의 계층 구조가 복잡해질 수 있으므로 설계를 신중하게 해야 합니다.
Abstract Mixin Class
- 추상 Mixin 클래스는 상속받는 클래스 또는 Mixin을 사용하는 클래스에서 반드시 구현해야 하는 메서드를 포함할 수 있습니다.
- 이 방식은 Mixin을 추상 클래스와 같이 사용할 수 있게 해주며, 다중 상속과 유사한 구조를 구현할 수 있습니다.
abstract mixin class Musician {
// 사용하려면 추상 메서드를 반드시 구현해야 함
void playInstrument(String instrumentName);
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
class Virtuoso with Musician { // Musician을 mixin으로 사용
void playInstrument(String instrumentName) {
print('Plays the $instrumentName beautifully');
}
}
class Novice extends Musician { // Musician을 class로 사용
void playInstrument(String instrumentName) {
print('Plays the $instrumentName poorly');
}
}
Extension과 Mixin의 차이점
주요 차이점
- Extension은 기존 클래스에 메서드나 속성을 추가합니다. 예를 들어,
String
타입에 새로운 메서드를 추가하여 문자열 처리 로직을 개선할 때 유용합니다.
Mixin은 여러 클래스에 공통 기능을 재사용하기 위해 사용됩니다. 예를 들어, 데이터 처리와 같은 기능을 여러 클래스에서 공유해야 하는 경우에 적합합니다. - Extension은 상태를 가질 수 없지만, Mixin은 상태를 가질 수 있습니다.
Extension 사용 예시
기본 타입 확장
예를 들어, 타입에 새 메서드를 추가하여 문자열이 숫자로만 구성되어 있는지 확인하는 메서드를 만들 수 있습니다:
extension StringValidator on String {
bool get isNumeric {
return RegExp(r'^-?[0-9]+$').hasMatch(this);
}
}
void main() {
String value = "12345";
print(value.isNumeric); // true
}
이 예시는 String
타입의 모든 문자열에서 isNumeric
메서드를 사용할 수 있게 해줍니다.
외부 라이브러리 클래스 확장
외부 라이브러리의 클래스를 확장하여 유틸리티 메서드를 추가할 수도 있습니다. 예를 들어, 외부 라이브러리에서 제공하는 클래스에 새로운 메서드를 추가하여 더욱 직관적으로 사용할 수 있습니다.
import 'package:some_external_library/external_class.dart';
extension ExternalClassExtension on ExternalClass {
void logData() {
print('Logging data: ${this.data}');
}
}
Mixin 사용 예시
상태 관리 기능 추가 Mixin은 상태를 관리할 수 있기 때문에, 여러 클래스에서 동일한 상태 관리 로직을 공유할 수 있습니다. 예를 들어, 동일한 상태 로직을 여러 UI 컴포넌트에 적용할 수 있습니다.
mixin CounterMixin {
int _count = 0;
void increment() {
_count++;
}
void reset() {
_count = 0;
}
int get count => _count;
}
class Counter with CounterMixin {
void displayCount() {
print('Current count: $count');
}
}
void main() {
Counter counter = Counter();
counter.increment();
counter.displayCount(); // Current count: 1
}
이 예시에서는 CounterMixin
을 사용해 여러 클래스에서 동일한 카운터 상태를 관리할 수 있도록 합니다.