본문 바로가기
Programming/Dart

Dart Interface

by #root 2025. 3. 27.

Dart 인터페이스(Interface) 설명 및 예제

1. 인터페이스란?

인터페이스(Interface)는 클래스가 특정 동작을 수행하도록 강제하는 계약(contract)입니다. Dart에서는 추상 클래스(abstract class) 를 사용하여 인터페이스를 정의할 수 있으며, 다른 클래스에서 이를 구현(implement) 할 수 있습니다.

Dart에서는 다른 객체 지향 언어(Java, C# 등)와 달리 interface 키워드가 없습니다. 대신, 모든 클래스가 암묵적으로 인터페이스 역할을 하며, implements 키워드를 사용하여 특정 클래스의 인터페이스를 구현할 수 있습니다.


2. Dart에서 인터페이스의 특징

  • Dart에서는 모든 클래스가 인터페이스 역할을 함.
  • implements 키워드를 사용하여 인터페이스를 구현할 수 있음.
  • 인터페이스를 구현할 경우, 모든 메서드와 변수를 반드시 재정의(override) 해야 함.
  • 다중 구현이 가능함(여러 개의 인터페이스를 동시에 구현 가능).

3. 인터페이스의 기본 사용법

Dart에서는 abstract class 를 사용하여 인터페이스를 정의하고, implements 키워드를 사용하여 이를 구현할 수 있습니다.

예제 1: 기본적인 인터페이스 구현

// 인터페이스 역할을 하는 추상 클래스
abstract class Animal {
  void makeSound(); // 추상 메서드
}

// Dog 클래스에서 Animal 인터페이스 구현
class Dog implements Animal {
  @override
  void makeSound() {
    print("멍멍!");
  }
}

// Cat 클래스에서 Animal 인터페이스 구현
class Cat implements Animal {
  @override
  void makeSound() {
    print("야옹~");
  }
}

void main() {
  Dog dog = Dog();
  Cat cat = Cat();

  dog.makeSound(); // 출력: 멍멍!
  cat.makeSound(); // 출력: 야옹~
}

설명

  • Animal 클래스는 추상 클래스이며, makeSound()라는 추상 메서드를 가짐.
  • Dog와 Cat 클래스는 Animal을 implements 하여 인터페이스를 구현함.
  • 인터페이스를 구현한 클래스는 반드시 makeSound() 메서드를 재정의해야 함.

4. 다중 인터페이스 구현

Dart에서는 여러 개의 인터페이스를 동시에 구현할 수 있습니다.

예제 2: 다중 인터페이스 구현

abstract class Flyable {
  void fly();
}

abstract class Swimmable {
  void swim();
}

// 새는 날 수도 있고, 수영도 가능하므로 두 인터페이스를 구현함
class Duck implements Flyable, Swimmable {
  @override
  void fly() {
    print("오리가 날아갑니다!");
  }

  @override
  void swim() {
    print("오리가 수영합니다!");
  }
}

void main() {
  Duck duck = Duck();
  duck.fly();  // 출력: 오리가 날아갑니다!
  duck.swim(); // 출력: 오리가 수영합니다!
}

설명

  • Flyable과 Swimmable 두 개의 인터페이스를 정의.
  • Duck 클래스가 Flyable, Swimmable을 모두 구현하여 다중 인터페이스를 사용.

5. 인터페이스 vs 상속의 차이점

Dart에서 extends(상속)와 implements(인터페이스 구현)는 다르게 동작합니다.

비교 항목 extends (상속) implements (인터페이스)

코드 재사용 부모 클래스의 메서드를 재사용 가능 모든 메서드를 새로 구현해야 함
다중 적용 단일 상속만 가능 (하나의 부모 클래스만 가질 수 있음) 여러 개의 인터페이스를 구현 가능
필드 상속 부모 클래스의 필드도 상속됨 인터페이스에서는 필드도 반드시 재정의 필요

예제 3: 상속과 인터페이스 비교

// 상속을 사용하는 경우
class Parent {
  void sayHello() {
    print("Hello from Parent");
  }
}

class Child extends Parent {}

void main() {
  Child child = Child();
  child.sayHello(); // 상속받은 메서드를 그대로 사용 가능
}
// 인터페이스를 사용하는 경우
abstract class Speaker {
  void sayHello();
}

class Person implements Speaker {
  @override
  void sayHello() {
    print("Hello from Person");
  }
}

void main() {
  Person person = Person();
  person.sayHello(); // 반드시 재정의 필요
}

설명

  • extends는 부모 클래스의 메서드를 그대로 사용할 수 있음.
  • implements는 모든 메서드를 반드시 재정의해야 함.

6. 인터페이스와 믹스인(Mixin) 비교

Dart에서는 Mixin을 사용하여 코드 재사용성을 높일 수도 있습니다. 인터페이스와 믹스인은 다르게 동작하므로, 적절한 상황에서 사용해야 합니다.

예제 4: 인터페이스 vs 믹스인

// 인터페이스 사용
abstract class Walker {
  void walk();
}

class Human implements Walker {
  @override
  void walk() {
    print("사람이 걷는다.");
  }
}

// 믹스인 사용
mixin Runner {
  void run() {
    print("빠르게 달립니다!");
  }
}

class Athlete with Runner {}

void main() {
  Human human = Human();
  human.walk(); // 출력: 사람이 걷는다.

  Athlete athlete = Athlete();
  athlete.run(); // 출력: 빠르게 달립니다!
}

차이점

  • implements는 인터페이스로 사용되며, 모든 메서드를 직접 구현해야 함.
  • mixin은 기존 클래스에 기능을 추가할 때 사용하며, 이미 구현된 메서드를 제공.

7. 실제 활용 예제: 서비스 인터페이스

Dart에서 인터페이스는 의존성 주입(DI, Dependency Injection)서비스 분리에 자주 사용됩니다.

예제 5: 데이터 저장소 인터페이스

// 데이터 저장소 인터페이스 정의
abstract class StorageService {
  void save(String key, String value);
  String? load(String key);
}

// 파일 저장소 구현
class FileStorage implements StorageService {
  Map<String, String> storage = {};

  @override
  void save(String key, String value) {
    storage[key] = value;
    print("파일에 저장: $key -> $value");
  }

  @override
  String? load(String key) {
    return storage[key];
  }
}

// 데이터베이스 저장소 구현
class DatabaseStorage implements StorageService {
  Map<String, String> db = {};

  @override
  void save(String key, String value) {
    db[key] = value;
    print("데이터베이스에 저장: $key -> $value");
  }

  @override
  String? load(String key) {
    return db[key];
  }
}

void main() {
  StorageService storage = FileStorage();
  storage.save("username", "dart_user");
  print(storage.load("username")); // 출력: dart_user
}

설명

  • StorageService 인터페이스를 정의하고, FileStorage와 DatabaseStorage에서 각각 구현.
  • 프로그램 실행 시 StorageService의 구현체를 선택하여 사용할 수 있음.

8. 결론

  • Dart에서는 abstract class 를 사용하여 인터페이스를 정의.
  • implements 를 사용하면 모든 메서드를 반드시 구현해야 함.
  • 다중 인터페이스 구현이 가능하여 유연한 설계를 지원.
  • 인터페이스는 다형성, 의존성 주입, 서비스 분리 등에서 활용됨.

Dart에서 인터페이스를 활용하면 유지보수성이 높고 확장성이 좋은 코드를 작성할 수 있습니다! 🚀

'Programming > Dart' 카테고리의 다른 글

Dart Abstract  (0) 2025.03.28
Dart Mixins  (0) 2025.03.28
Dart Override  (0) 2025.03.27
Dart Inheritance  (1) 2025.03.25
Dart 파일 구조  (0) 2025.03.25