믹스인 vs 인터페이스
Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 55번째.
여러분은 믹스인(mixins)이 상속(inheritance)과 어떻게 비교되는지 살펴보았습니다. 이제 믹스인과 인터페이스(interfaces)를 비교해 보겠습니다. 두 가지 모두 여러 타입을 다룰 수 있게 해주기 때문입니다. 핵심적인 차이점은 이들이 무엇을 제공하느냐에 있습니다. 인터페이스는 규약(contract)을 정의하는 반면, 믹스인은 구현(implementation)을 제공합니다.
인터페이스를 구현할 때는 특정 메서드를 제공하겠다고 약속하지만, 모든 코드는 직접 작성해야 합니다. 믹스인을 사용하면 실제 코드를 그대로 제공받을 수 있습니다.
// 인터페이스 방식 - 모든 것을 구현해야 합니다
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // 직접 작성합니다
}
}
// 믹스인 방식 - 구현이 포함되어 있습니다
mixin Printable {
void printInfo() {
print('Printing...'); // 이미 작성되어 있습니다
}
}
class Report with Printable {} // 자동으로 printInfo()를 가져옵니다그렇다면 각각을 언제 사용해야 할까요? 서로 다른 클래스들이 동일한 메서드 시그니처를 필요로 하지만 서로 다른 구현이 필요할 때는 인터페이스(interface)를 사용하세요. 클래스들이 정확히 동일한 동작을 공유해야 할 때는 믹스인(mixin)을 사용하세요.
| 기능 | 인터페이스 | 믹스인 |
|---|---|---|
| 제공 | 명세만 제공 | 명세 + 코드 |
| 구현 | 직접 작성 | 포함됨 |
| 키워드 | implements | with |
| 용도 | 서로 다른 동작, 동일한 API | 공통 동작 |
두 가지 접근 방식을 결합할 수도 있습니다. 인터페이스를 사용하여 클래스가 수행할 수 있는 작업을 정의하고, 적절한 경우 믹스인(mixin)을 사용하여 공통 구현을 제공할 수 있습니다. Dart에서 두 가지를 결합할 때는 항상 implements 앞에 with를 배치하십시오:
class MyClass with SomeMixin implements SomeInterface {
// with가 implements보다 먼저 옵니다
}챌린지
쉬움인터페이스와 믹스인(mixin)을 언제 사용해야 하는지 보여주는 알림 시스템을 구축해 보겠습니다. 서로 다른 구현이 필요한 알림을 위한 인터페이스와, 모든 알림 유형에서 동일하게 작동해야 하는 공유 로깅 동작을 위한 믹스인을 만들 것입니다.
코드는 세 개의 파일로 구성됩니다:
notifiable.dart: 인터페이스 역할을 하는 추상 클래스Notifiable을 정의합니다. 이 클래스는 두 개의 추상 메서드send(String message)와String을 반환하는getChannel()을 선언해야 합니다. 서로 다른 알림 유형은 특정 채널에 따라 이 메서드들을 다르게 구현하게 됩니다.logging.dart: 모든 알림이 가져야 할 공유 동작을 제공하는Logging믹스인을 만듭니다.[LOG] Sent via [channel]: [message]를 출력하는logSent(String channel, String message)메서드와[LOG] Timestamp recorded를 출력하는logTimestamp()메서드를 포함하세요. 이 동작은 모든 알림 유형에서 동일하므로 믹스인으로 만들기에 완벽합니다.main.dart: 두 파일을 모두 임포트하고 인터페이스와 믹스인을 결합한 두 개의 알림 클래스를 만듭니다:Logging믹스인을 사용하고Notifiable을 구현하는EmailNotification클래스. 이 클래스의getChannel()은'Email'을 반환해야 하며,send()메서드는Sending email: [message]를 출력한 다음, 채널과 메시지를 인자로logSent()를 호출하고, 마지막으로logTimestamp()를 호출해야 합니다.Logging믹스인을 사용하고Notifiable을 구현하는SMSNotification클래스. 이 클래스의getChannel()은'SMS'를 반환해야 하며,send()메서드는Sending SMS: [message]를 출력한 다음, 채널과 메시지를 인자로logSent()를 호출하고, 마지막으로logTimestamp()를 호출해야 합니다.
EmailNotification인스턴스를 생성하고send('Hello World')를 호출한 뒤, 빈 줄을 하나 출력하고,SMSNotification인스턴스를 생성하여send('Quick update')를 호출하세요.
인터페이스가 각 클래스에 고유한 send() 및 getChannel() 구현(서로 다른 동작)을 강제하는 반면, 믹스인은 두 클래스 모두에서 동일하게 작동하는 로깅 메서드(공유 동작)를 제공하는 방식에 주목하세요.
예상 출력 결과:
Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded
Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recorded치트 시트
인터페이스는 계약(메서드 시그니처)을 정의하는 반면, 믹스인(mixin)은 구현(실제 코드)을 제공합니다.
인터페이스를 구현할 때는 모든 코드를 직접 작성해야 합니다:
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // 직접 작성합니다
}
}믹스인을 사용하면 구현을 그대로 가져다 쓸 수 있습니다:
mixin Printable {
void printInfo() {
print('Printing...'); // 이미 작성되어 있습니다
}
}
class Report with Printable {} // 자동으로 printInfo()를 가집니다각각 언제 사용해야 하는가:
- 서로 다른 클래스들이 동일한 메서드 시그니처를 필요로 하지만 구현 방식은 달라야 할 때 인터페이스를 사용하세요.
- 클래스들이 정확히 동일한 동작을 공유해야 할 때 믹스인을 사용하세요.
| 기능 | 인터페이스 | 믹스인 |
|---|---|---|
| 제공 사항 | 계약만 제공 | 계약 + 코드 |
| 구현 | 직접 작성 | 포함됨 |
| 키워드 | implements | with |
| 용도 | 동일한 API, 다른 동작 | 공유된 동작 |
두 가지 방식을 결합할 수 있습니다. 결합할 때는 항상 implements 앞에 with를 작성하세요:
class MyClass with SomeMixin implements SomeInterface {
// with가 implements보다 먼저 옵니다
}직접 해보기
import 'notifiable.dart';
import 'logging.dart';
// TODO: 다음을 수행하는 EmailNotification 클래스를 생성하세요:
// - Logging 믹스인을 사용 (with)
// - Notifiable을 구현 (implements)
// - getChannel()이 'Email'을 반환
// - send()가 'Sending email: [message]'를 출력한 후, logSent()와 logTimestamp()를 호출
// TODO: 다음을 수행하는 SMSNotification 클래스를 생성하세요:
// - Logging 믹스인을 사용 (with)
// - Notifiable을 구현 (implements)
// - getChannel()이 'SMS'를 반환
// - send()가 'Sending SMS: [message]'를 출력한 후, logSent()와 logTimestamp()를 호출
void main() {
// TODO: EmailNotification을 생성하고 send('Hello World')를 호출하세요
// TODO: 빈 줄을 출력하세요
// TODO: SMSNotification을 생성하고 send('Quick update')를 호출하세요
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.