# 추상 클래스와 인터페이스

#### 추상 클래스란 무엇인가?

추상 클래스는 인스턴스를 만들 수 없는 클래스이다. 즉, **new** 키워드를 사용하여 객체를 생성할 수 없으며, 상속을 통해서만 사용될 수 있다. 추상 클래스는 하나 이상의 추상 메소드를 포함할 수 있으며, 이 메소드는 구현되지 않고 서브클래스에서 반드시 구현해야 한다. 이를 통해 공통적인 기능을 정의하면서도 세부 구현은 서브클래스에 맡기는 방식으로 유연하게 동작할 수 있다.

**예시:**

```dart
abstract class Animal {
  void makeSound();  // 추상 메소드
}
```

#### 추상 클래스의 역할

추상 클래스는 객체 지향 프로그래밍에서 중요한 역할을 한다. 특히, 다양한 클래스들이 공통적으로 가져야 할 속성이나 메소드를 정의하는 데 유용하다. 추상 클래스는 설계 단계에서 클래스들의 구조를 정의하는 데 도움을 준다.

#### 인터페이스란 무엇인가?

Dart에서는 별도의 **interface** 키워드를 제공하지 않는다. 대신, 모든 클래스는 암묵적으로 인터페이스 역할을 할 수 있다. **implements** 키워드를 사용하여 클래스를 구현할 때, 해당 클래스의 모든 메소드를 구현해야 한다. 인터페이스는 특정 기능을 다중 상속하고자 할 때 유용하게 사용된다.

**예시:**

```dart
class Dog implements Animal {
  @override
  void makeSound() {
    print("Bark");
  }
}
```

#### 추상 클래스와 인터페이스의 차이점

추상 클래스와 인터페이스는 모두 객체 지향 설계에서 중요한 역할을 하지만, 그 목적과 사용 방식에는 차이가 있다.

1. **추상 클래스**는 상속을 통해 기능을 공유하며, 공통된 코드도 포함할 수 있다. 반면, **인터페이스**는 클래스가 구현해야 할 메소드의 계약을 정의한다.
2. **추상 클래스**는 하나만 상속받을 수 있지만, **인터페이스**는 여러 개를 구현할 수 있다.
3. 추상 클래스는 메소드와 필드를 가질 수 있지만, 인터페이스는 오직 메소드 선언만 갖는다.

**추상 클래스 예시:**

```dart
abstract class Vehicle {
  void drive();
  void stop();
}
```

**인터페이스 예시:**

```dart
class Car implements Vehicle {
  @override
  void drive() {
    print("Driving the car");
  }

  @override
  void stop() {
    print("Car stopped");
  }
}
```

#### 추상 클래스와 인터페이스의 사용 사례

**추상 클래스의 사용 사례**

추상 클래스는 계층 구조에서 공통적인 동작이나 속성을 정의하고, 이를 서브클래스에서 확장하거나 재정의할 때 유용하다. 특히, 코드 재사용성이 필요한 경우에 효과적이다. 예를 들어, 동물 클래스를 상속받는 다양한 동물들이 공통적으로 가질 수 있는 행동이나 속성을 추상 클래스에 정의하고, 각 동물이 해당 행동을 어떻게 구현할지를 서브클래스에서 결정할 수 있다.

**예시:**

```dart
abstract class Animal {
  String name;
  
  Animal(this.name);
  
  void eat() {
    print('$name is eating.');
  }
  
  void makeSound();  // 추상 메소드
}

class Dog extends Animal {
  Dog(String name) : super(name);
  
  @override
  void makeSound() {
    print('Bark');
  }
}

class Cat extends Animal {
  Cat(String name) : super(name);
  
  @override
  void makeSound() {
    print('Meow');
  }
}
```

이 경우, **Animal** 클래스는 추상 클래스이며, 공통적으로 동물들이 가진 **eat()** 메소드를 구현하고 있다. 하지만 **makeSound()** 메소드는 추상 메소드로, 각각의 동물들이 다르게 구현해야 한다.

**인터페이스의 사용 사례**

인터페이스는 서로 다른 클래스들에 공통된 기능을 강제할 때 유용하다. 예를 들어, 여러 장치들이 서로 다른 방식으로 작동하지만, **start**와 **stop** 같은 공통 기능을 가져야 한다고 가정할 수 있다. 이때 **인터페이스**를 통해 이 두 메소드를 강제할 수 있다.

**예시:**

```dart
class Device {
  void start() {
    print('Device started');
  }
  
  void stop() {
    print('Device stopped');
  }
}

class Printer implements Device {
  @override
  void start() {
    print('Printer is warming up');
  }
  
  @override
  void stop() {
    print('Printer is cooling down');
  }
}

class Scanner implements Device {
  @override
  void start() {
    print('Scanner is initializing');
  }
  
  @override
  void stop() {
    print('Scanner is shutting down');
  }
}
```

이 예시에서는 **Device** 클래스가 인터페이스 역할을 하고 있으며, 각각의 장치인 **Printer**와 **Scanner**가 이를 구현하여 \*\*start()\*\*와 **stop()** 메소드를 각기 다르게 정의하고 있다.

#### 인터페이스와 추상 클래스의 선택 기준

인터페이스와 추상 클래스는 각각의 상황에 맞게 선택적으로 사용된다. 두 방법 모두 클래스 간의 관계를 정의하고, 코드의 재사용성을 높일 수 있지만, 특정 상황에서 더 적합한 방법을 선택해야 한다.

* **추상 클래스**는 공통된 속성과 동작을 미리 정의해 놓고, 이를 확장하여 사용하는 경우 적합한다.
* **인터페이스**는 다중 구현이 필요한 경우 적합하며, 클래스 간 결합도를 낮출 수 있다.

**다중 상속 문제 해결**

Dart는 클래스의 다중 상속을 지원하지 않지만, 여러 인터페이스를 구현할 수 있다. 이 방법을 통해 클래스 간의 유연한 관계를 설정하고, 다중 상속의 문제를 우회할 수 있다.

**예시:**

```dart
class Flyable {
  void fly();
}

class Swimmable {
  void swim();
}

class Duck implements Flyable, Swimmable {
  @override
  void fly() {
    print('Duck is flying');
  }
  
  @override
  void swim() {
    print('Duck is swimming');
  }
}
```

이처럼 Duck 클래스는 Flyable과 Swimmable 인터페이스를 모두 구현할 수 있다.

#### 추상 클래스와 인터페이스의 혼합 사용

때로는 **추상 클래스**와 **인터페이스**를 함께 사용하는 것이 유용할 수 있다. 예를 들어, 특정 클래스는 공통적인 동작을 상속받으면서도 여러 기능 인터페이스를 구현해야 할 경우이다. 이렇게 하면 코드의 유연성과 재사용성을 높일 수 있다.

**예시: 추상 클래스와 인터페이스의 혼합**

```dart
abstract class Machine {
  void start();
  void stop();
}

class Maintenance {
  void service() {
    print('Performing maintenance.');
  }
}

class Car extends Machine implements Maintenance {
  @override
  void start() {
    print('Car is starting.');
  }

  @override
  void stop() {
    print('Car is stopping.');
  }

  @override
  void service() {
    print('Car is being serviced.');
  }
}
```

이 예시에서 **Car** 클래스는 **Machine**이라는 추상 클래스를 상속받아 공통 기능인 \*\*start()\*\*와 \*\*stop()\*\*을 구현한다. 동시에 **Maintenance** 인터페이스를 구현하여 유지 보수 기능을 포함할 수 있다.

#### 인터페이스의 상속

인터페이스 역시 상속을 통해 확장될 수 있다. 이를 통해 인터페이스 간의 계층 구조를 만들고, 더 복잡한 설계를 할 수 있다.

**인터페이스 상속 예시**

```dart
class Drivable {
  void drive();
}

class ElectricDrivable extends Drivable {
  void charge();
}

class ElectricCar implements ElectricDrivable {
  @override
  void drive() {
    print('Electric car is driving.');
  }

  @override
  void charge() {
    print('Electric car is charging.');
  }
}
```

이 예시에서 **ElectricDrivable** 인터페이스는 **Drivable** 인터페이스를 상속하여 확장되었다. **ElectricCar**는 **ElectricDrivable**를 구현하여 \*\*drive()\*\*와 **charge()** 메소드를 모두 가져오게 된다.

#### Dart에서의 다중 인터페이스 구현

Dart에서 다중 상속을 허용하지 않기 때문에, 여러 인터페이스를 구현하여 다중 상속의 효과를 낼 수 있다. 이러한 기능은 특정 클래스가 여러 인터페이스의 역할을 동시에 수행해야 할 때 유용하다. 이를 통해 보다 복잡하고 확장 가능한 시스템을 설계할 수 있다.

**다중 인터페이스 구현 예시**

```dart
class Printable {
  void printContent();
}

class Scannable {
  void scanDocument();
}

class MultifunctionalPrinter implements Printable, Scannable {
  @override
  void printContent() {
    print('Printing document.');
  }

  @override
  void scanDocument() {
    print('Scanning document.');
  }
}
```

위 예시에서 **MultifunctionalPrinter** 클래스는 **Printable**과 **Scannable** 두 개의 인터페이스를 구현하여, 출력과 스캔 두 가지 기능을 모두 수행할 수 있다.
