본문 바로가기
카테고리 없음

Dart에서 추상 클래스 이해하기

by 빛나는개발자 2024. 9. 24.
728x90
반응형

Dart에서 추상 클래스(Abstract Class)는 객체 지향 프로그래밍에서 중요한 개념 중 하나로, 클래스 설계의 유연성과 확장성을 높이는 데 사용됩니다. 추상 클래스는 여러 클래스가 상속할 수 있지만, 직접 인스턴스화(객체 생성)는 할 수 없습니다. 이는 추상 클래스가 기본적인 인터페이스 또는 행동을 정의하지만, 구체적인 구현은 서브 클래스에서 제공해야 하기 때문입니다. 이를 통해 프로그램의 유지보수성과 코드 재사용성을 높일 수 있으며, 다형성(Polymorphism)과 같은 개념을 활용하여 더욱 유연한 설계가 가능합니다.

1. 추상 클래스란?

추상 클래스는 다른 클래스가 상속하여 확장할 수 있는 클래스입니다. 하지만 추상 클래스 자체는 인스턴스화할 수 없습니다. 즉, new 키워드를 사용해 직접 객체를 생성하려고 하면 컴파일 오류가 발생합니다. 추상 클래스는 공통된 속성이나 메서드를 정의하고, 이를 상속받은 클래스들이 그 구체적인 동작을 정의하도록 강제할 수 있습니다.

추상 클래스 정의 방법

Dart에서 추상 클래스는 abstract 키워드를 사용하여 정의됩니다. 추상 클래스는 하나 이상의 추상 메서드를 가질 수 있으며, 이러한 메서드는 구현이 없는 메서드로, 단순히 메서드의 시그니처만 정의하고 실제 구현은 서브 클래스에서 하게 됩니다.

abstract class Animal {
  void makeSound(); // 추상 메서드: 구현이 없음
}

위의 코드에서 Animal은 추상 클래스입니다. 이 클래스는 makeSound()라는 메서드를 정의하고 있지만, 그 메서드는 구현되지 않았습니다. 이를 상속받는 클래스는 반드시 이 메서드를 구현해야 합니다.

2. 추상 클래스 상속

추상 클래스를 상속받는 클래스는 추상 클래스의 모든 추상 메서드를 반드시 구현해야 합니다. 상속을 통해 자식 클래스는 부모 클래스의 속성과 메서드를 물려받아 사용할 수 있으며, 추상 클래스에서 정의된 기본 메서드들을 재정의(override)할 수 있습니다.

추상 클래스 상속 예시

다음은 추상 클래스 Animal을 상속받아 구체적인 동작을 구현한 예시입니다:

class Dog extends Animal {
  @override
  void makeSound() {
    print('Woof Woof'); // 구체적인 구현
  }
}

class Cat extends Animal {
  @override
  void makeSound() {
    print('Meow'); // 구체적인 구현
  }
}

위 코드에서 Dog와 Cat 클래스는 Animal 추상 클래스를 상속받았으며, makeSound() 메서드를 구체적으로 구현하였습니다. 이처럼 상속을 통해 각 동물 클래스는 고유의 소리를 출력할 수 있게 됩니다.

3. 추상 클래스의 인스턴스화 제한

추상 클래스는 직접 인스턴스화할 수 없습니다. 즉, 추상 클래스는 기본 설계 원칙으로 정의된 템플릿일 뿐, 실제 동작을 담당하지 않으므로 이를 직접 사용할 수 없습니다.

인스턴스화 시도 시 오류 예시

void main() {
  Animal myAnimal = Animal(); // 오류 발생: 추상 클래스는 인스턴스화할 수 없음
}

위의 코드는 컴파일 오류를 발생시킵니다. 추상 클래스는 상속을 통해 구체적인 클래스를 만들고, 그 클래스를 인스턴스화해야 합니다. 예를 들어, Dog 클래스는 인스턴스화할 수 있습니다.

void main() {
  Animal myDog = Dog();
  myDog.makeSound(); // Output: Woof Woof
}

이처럼 추상 클래스는 상속받은 서브 클래스의 구체적인 동작을 통해 사용됩니다.

4. 추상 클래스의 역할

1) 코드 재사용성 향상

추상 클래스는 상속을 통해 코드를 재사용할 수 있는 기초를 제공합니다. 여러 서브 클래스가 공통으로 사용하는 로직을 부모 추상 클래스에 정의함으로써, 서브 클래스는 필요한 부분만 재정의하거나 확장할 수 있습니다.

abstract class Animal {
  void breathe() {
    print('Breathing');
  }

  void makeSound();
}

class Dog extends Animal {
  @override
  void makeSound() {
    print('Woof Woof');
  }
}

class Cat extends Animal {
  @override
  void makeSound() {
    print('Meow');
  }
}

위 코드에서 breathe() 메서드는 추상 클래스 Animal에 정의되어 있고, 모든 동물이 공유하는 동작이므로 서브 클래스에서 별도의 구현 없이 그대로 사용할 수 있습니다. 반면 makeSound()는 서브 클래스에서 각자의 방식으로 구현해야 합니다.

2) 강제 구현

추상 클래스는 서브 클래스에서 반드시 구현해야 하는 메서드를 정의할 수 있습니다. 이를 통해 특정 기능의 구현을 강제할 수 있으며, 이를 통해 애플리케이션의 일관성과 안정성을 보장할 수 있습니다.

5. 다형성(Polymorphism)과 추상 클래스

다형성(Polymorphism)은 객체 지향 프로그래밍에서 매우 중요한 개념으로, 하나의 인터페이스나 추상 클래스를 통해 여러 다른 클래스의 객체를 동일하게 다룰 수 있도록 합니다. 추상 클래스는 다형성을 구현하는 데 자주 사용되며, 코드의 유연성과 확장성을 높이는 데 기여합니다.

다형성 예시

abstract class Animal {
  void makeSound();
}

class Dog extends Animal {
  @override
  void makeSound() {
    print('Woof Woof');
  }
}

class Cat extends Animal {
  @override
  void makeSound() {
    print('Meow');
  }
}

void animalSound(Animal animal) {
  animal.makeSound();
}

void main() {
  Animal myAnimal = Dog();
  animalSound(myAnimal); // Output: Woof Woof
  
  myAnimal = Cat();
  animalSound(myAnimal); // Output: Meow
}

이 예시에서는 animalSound() 함수가 Animal 타입의 객체를 인자로 받아 해당 객체의 makeSound() 메서드를 호출합니다. 다형성 덕분에 Dog와 Cat 클래스의 객체를 모두 동일하게 다룰 수 있으며, 각각의 클래스에서 구현한 고유한 makeSound() 메서드가 실행됩니다.

6. 추상 클래스와 인터페이스의 차이

Dart에서 인터페이스는 특별한 키워드 없이도 구현될 수 있으며, Dart의 모든 클래스는 암묵적으로 인터페이스로 작동할 수 있습니다. 추상 클래스와 인터페이스는 비슷한 역할을 하지만, 추상 클래스는 일부 구현을 포함할 수 있다는 점에서 인터페이스와 차이가 있습니다. 인터페이스는 오직 메서드 시그니처만 제공하며, 구현을 포함하지 않습니다. 추상 클래스는 기본 동작을 포함할 수 있어 좀 더 유연한 구조를 제공합니다.

예시: 인터페이스와 추상 클래스의 차이

abstract class Animal {
  void breathe() {
    print('Breathing');
  }

  void makeSound();
}

class Bird implements Animal {
  @override
  void makeSound() {
    print('Tweet');
  }

  @override
  void breathe() {
    print('Bird is breathing');
  }
}

위 예시에서 Animal 추상 클래스는 Bird 클래스에 의해 구현되었습니다. breathe() 메서드는 추상 클래스에 기본 구현이 있지만, Bird 클래스에서 이를 재정의할 수 있습니다. 인터페이스와 달리 추상 클래스는 이처럼 기본 동작을 제공할 수 있습니다.

7. 결론

Dart에서 추상 클래스는 강력한 객체 지향 프로그래밍 도구로, 코드의 재사용성과 유지보수성을 크게 향상시킵니다. 추상 클래스는 공통 동작을 상속받는 여러 클래스가 공유할 수 있는 기초를 제공하며, 특정 메서드의 구현을 강제할 수 있습니다. 다형성을 통해 추상 클래스를 여러 방식으로 구현한 객체들을 동일하게 다룰 수 있어, 코드의 유연성과 확장성이 극대화됩니다. Dart에서 추상 클래스는 복잡한 애플리케이션의 구조를 효율적으로 관리할 수 있는 중요한 개념입니다.

728x90
반응형