인터페이스를 통한 다형성
Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 58번째.
이전 레슨에서 여러분은 상속을 통한 다형성, 즉 부모 클래스 타입을 사용하여 자식 객체를 다루는 것을 보았습니다. 하지만 다형성은 인터페이스를 통해서도 작동하며, 이 방식은 종종 더 유연합니다.
클래스가 동일한 인터페이스를 구현할 때, 상속 관계가 없더라도 다형적으로 처리할 수 있습니다:
abstract class Printable {
void printDetails();
}
class Invoice implements Printable {
@override
void printDetails() => print('Invoice #1234');
}
class Report implements Printable {
@override
void printDetails() => print('Monthly Report');
}
class Receipt implements Printable {
@override
void printDetails() => print('Receipt for purchase');
}
void printAll(List<Printable> items) {
for (var item in items) {
item.printDetails();
}
}
void main() {
var documents = [Invoice(), Report(), Receipt()];
printAll(documents);
}Invoice, Report, 그리고 Receipt는 완전히 서로 관련이 없는 클래스들입니다 - 이들은 부모를 공유하지 않습니다. 하지만 이들은 모두 Printable을 구현하므로, 우리는 이들을 동일한 리스트에 저장하고 각각에 대해 printDetails()를 호출할 수 있습니다.
이것은 상속 계층 구조를 강제하지 않고도 동작 계약을 정의할 수 있게 해주기 때문에 강력합니다. 어떤 클래스든 무엇을 확장(extends)하든 상관없이 인터페이스를 구현할 수 있습니다. 기존 코드를 수정하지 않고도 새로운 printable 타입을 추가할 수 있으므로 코드가 더 모듈화됩니다. 그저 인터페이스를 구현하기만 하면 됩니다.
챌린지
쉬움인터페이스를 통해 다형성을 보여주는 미디어 플레이어 시스템을 구축해 보겠습니다. "재생 가능함"이 무엇을 의미하는지 정의하는 인터페이스를 만든 다음, 상속 관계가 없음에도 불구하고 이 계약을 모두 충족하여 일관되게 처리될 수 있는 여러 관련 없는 미디어 유형을 구현하게 됩니다.
코드를 두 개의 파일로 구성합니다:
media.dart: 여기에 인터페이스와 미디어 클래스를 정의합니다:- 두 개의 추상 메서드
play()와String getMediaType()을 가진 추상 클래스Playable Playable을 구현하는Song클래스.String title과String artist를 가져야 합니다.play()메서드는Playing song: [title] by [artist]를 출력하고getMediaType()은'Audio'를 반환합니다.Playable을 구현하는Video클래스.String title과int duration(분 단위)을 가져야 합니다.play()메서드는Playing video: [title] ([duration] min)을 출력하고getMediaType()은'Video'를 반환합니다.Playable을 구현하는Podcast클래스.String title과int episode를 가져야 합니다.play()메서드는Playing podcast: [title] - Episode [episode]를 출력하고getMediaType()은'Podcast'를 반환합니다.
[Media Type]:을 출력한 후 해당 항목의play()를 호출하는playAll(List<Playable> playlist)함수를 만듭니다.- 두 개의 추상 메서드
main.dart: 미디어 파일을 임포트하고 인터페이스 기반 다형성을 보여줍니다:- 제목이
'Imagine'이고 아티스트가'John Lennon'인Song, 제목이'Dart Tutorial'이고 재생 시간이15인Video, 제목이'Tech Talk'이고 에피소드가42인Podcast를 포함하는List<Playable>을 생성합니다. - 이 리스트를
playAll()에 전달합니다.
- 제목이
Song, Video, Podcast가 부모 클래스를 공유하지 않는 완전히 무관한 유형임에 주목하세요. 하지만 모두 Playable을 구현하기 때문에 동일한 리스트에 저장하고 인터페이스를 통해 일관되게 처리할 수 있습니다.
예상 출력:
Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42치트 시트
다형성은 인터페이스를 통해 작동하며, 상속 관계가 없는 서로 관련 없는 클래스들을 일관되게 처리할 수 있게 해줍니다.
동일한 인터페이스를 구현하는 클래스들은 다형적으로 사용될 수 있습니다:
abstract class Printable {
void printDetails();
}
class Invoice implements Printable {
@override
void printDetails() => print('Invoice #1234');
}
class Report implements Printable {
@override
void printDetails() => print('Monthly Report');
}
void printAll(List<Printable> items) {
for (var item in items) {
item.printDetails();
}
}
void main() {
var documents = [Invoice(), Report()];
printAll(documents);
}인터페이스 기반 다형성을 통해 다음을 수행할 수 있습니다:
- 상속 계층 구조를 강제하지 않고 동작 계약을 정의합니다
- 서로 관련 없는 타입들을 동일한 컬렉션에 저장합니다
- 기존 코드를 수정하지 않고 새로운 타입을 추가합니다
- 코드를 더 모듈화하고 유연하게 만듭니다
직접 해보기
import 'media.dart';
void main() {
// TODO: 다음을 포함하는 List<Playable>을 생성하세요:
// - 제목이 'Imagine'이고 아티스트가 'John Lennon'인 Song
// - 제목이 'Dart Tutorial'이고 재생 시간이 15인 Video
// - 제목이 'Tech Talk'이고 에피소드가 42인 Podcast
// TODO: 리스트를 playAll()에 전달하세요
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.