Menu
Coddy logo textTech

인터페이스를 통한 다형성

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 타입을 추가할 수 있으므로 코드가 더 모듈화됩니다. 그저 인터페이스를 구현하기만 하면 됩니다.

challenge icon

챌린지

쉬움

인터페이스를 통해 다형성을 보여주는 미디어 플레이어 시스템을 구축해 보겠습니다. "재생 가능함"이 무엇을 의미하는지 정의하는 인터페이스를 만든 다음, 상속 관계가 없음에도 불구하고 이 계약을 모두 충족하여 일관되게 처리될 수 있는 여러 관련 없는 미디어 유형을 구현하게 됩니다.

코드를 두 개의 파일로 구성합니다:

  • media.dart: 여기에 인터페이스와 미디어 클래스를 정의합니다:
    • 두 개의 추상 메서드 play()String getMediaType()을 가진 추상 클래스 Playable
    • Playable을 구현하는 Song 클래스. String titleString artist를 가져야 합니다. play() 메서드는 Playing song: [title] by [artist]를 출력하고 getMediaType()'Audio'를 반환합니다.
    • Playable을 구현하는 Video 클래스. String titleint duration(분 단위)을 가져야 합니다. play() 메서드는 Playing video: [title] ([duration] min)을 출력하고 getMediaType()'Video'를 반환합니다.
    • Playable을 구현하는 Podcast 클래스. String titleint episode를 가져야 합니다. play() 메서드는 Playing podcast: [title] - Episode [episode]를 출력하고 getMediaType()'Podcast'를 반환합니다.
    또한 리스트를 순회하며 각 항목에 대해 [Media Type]:을 출력한 후 해당 항목의 play()를 호출하는 playAll(List<Playable> playlist) 함수를 만듭니다.
  • main.dart: 미디어 파일을 임포트하고 인터페이스 기반 다형성을 보여줍니다:
    • 제목이 'Imagine'이고 아티스트가 'John Lennon'Song, 제목이 'Dart Tutorial'이고 재생 시간이 15Video, 제목이 'Tech Talk'이고 에피소드가 42Podcast를 포함하는 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()에 전달하세요
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨