IMCAT.DEV
로그인 가입
Dart 중급 객체지향

Dart 상속과 다형성

Dart 상속과 다형성 — 중급

상속과 다형성

1. 도입 (왜 배워야 하는가)

상속과 다형성은 객체지향 프로그래밍(OOP)의 두 가지 중요한 개념입니다. 이 두 가지를 이해하면 코드 재사용성, 유지보수성 및 확장성을 향상시킬 수 있습니다. 예를 들어, 쇼핑몰 애플리케이션에서 다양한 상품을 다룰 때, 상속을 활용하면 반복적인 코드를 줄일 수 있습니다. 만약 이 개념을 모르고 디자인을 한다면, 유사한 코드가 여러 번 반복되어 코드가 복잡해지고 오류가 발생할 가능성이 높아집니다.

2. 핵심 개념 설명

상속

상속은 한 클래스가 다른 클래스의 속성과 메서드를 물려받는 것입니다. 예를 들어, '동물'이라는 클래스가 있을 때 '개'와 '고양이'는 '동물' 클래스를 상속받을 수 있습니다. 이때 '개'와 '고양이'는 '동물'의 속성과 메서드를 공유합니다.

다형성

다형성은 다양한 클래스의 객체가 동일한 인터페이스를 사용하여 서로 다른 방식으로 동작할 수 있게 해주는 기능입니다. 예를 들어, '동물' 클래스의 '소리 내기' 메서드를 '개'는 "멍멍", '고양이'는 "야옹"으로 구현할 수 있습니다.

용어정의
상속클래스 간의 관계로, 자식 클래스가 부모 클래스의 특성을 물려받는 것
다형성동일한 메서드 이름을 가지고 있지만, 각기 다른 클래스에서 다르게 동작하는 것

3. 기본 코드 예제

다음은 상속과 다형성을 활용한 간단한 코드 예제입니다.

class Animal {
  void sound() {
    print("동물의 소리");
  }
}

class Dog extends Animal {
  @override
  void sound() {
    print("멍멍");
  }
}

class Cat extends Animal {
  @override
  void sound() {
    print("야옹");
  }
}

void main() {
  Animal myDog = Dog();
  Animal myCat = Cat();

  myDog.sound(); // 멍멍
  myCat.sound(); // 야옹
}

실행 결과

멍멍
야옹

코드 한 줄씩 동작 설명

  1. class Animal : 'Animal' 클래스를 정의합니다.
  2. void sound() : 기본 소리 메서드를 정의합니다.
  3. class Dog extends Animal : 'Dog' 클래스가 'Animal' 클래스를 상속받습니다.
  4. @override void sound() : 'Dog' 클래스에서 'sound()' 메서드를 재정의합니다.
  5. class Cat extends Animal : 'Cat' 클래스도 'Animal' 클래스를 상속받습니다.
  6. @override void sound() : 'Cat' 클래스에서 'sound()' 메서드를 재정의합니다.
  7. void main() : 프로그램의 시작점인 메인 메서드입니다.
  8. Animal myDog = Dog(); : 'Dog' 객체를 'Animal' 타입으로 생성합니다.
  9. Animal myCat = Cat(); : 'Cat' 객체를 'Animal' 타입으로 생성합니다.
  10. myDog.sound(); : 'Dog' 객체의 'sound()' 메서드를 호출합니다.
  11. myCat.sound(); : 'Cat' 객체의 'sound()' 메서드를 호출합니다.
  12. 4. 실전 활용 예제

    예제 1: 쇼핑몰 장바구니 구현

    쇼핑몰에서 다양한 상품을 장바구니에 추가할 때, 상품의 종류에 따라 가격을 계산할 수 있습니다.

    abstract class Product {
      double getPrice();
    }
    
    class Clothing extends Product {
      @override
      double getPrice() {
        return 20.0; // 의류 가격
      }
    }
    
    class Electronics extends Product {
      @override
      double getPrice() {
        return 200.0; // 전자제품 가격
      }
    }
    
    void main() {
      List<Product> cart = [Clothing(), Electronics()];
      double totalPrice = cart.fold(0, (sum, item) => sum + item.getPrice());
      print("총 가격: \$${totalPrice}");
    }

    실행 결과

    총 가격: $220.0

    예제 2: 게임 캐릭터 구현

    게임에서 다양한 캐릭터가 서로 다른 능력을 발휘할 수 있습니다.

    abstract class Character {
      void attack();
    }
    
    class Warrior extends Character {
      @override
      void attack() {
        print("전사가 공격합니다!");
      }
    }
    
    class Mage extends Character {
      @override
      void attack() {
        print("마법사가 공격합니다!");
      }
    }
    
    void main() {
      List<Character> characters = [Warrior(), Mage()];
      for (var character in characters) {
        character.attack();
      }
    }

    실행 결과

    전사가 공격합니다!
    마법사가 공격합니다!

    5. 흔한 실수와 주의사항

    실수 1: 메서드 오버라이드 시 @override 생략

    • 잘못된 코드:
    • class Dog extends Animal {
        void sound() { // @override가 없음
          print("멍멍");
        }
      }
    • 올바른 코드:
    • class Dog extends Animal {
        @override
        void sound() {
          print("멍멍");
        }
      }

      실수 2: 상속 관계를 잘못 이해하기

    • 잘못된 코드:
    • class Dog extends Cat { // Dog가 Cat를 상속받음
      }
    • 올바른 코드:
    • class Dog extends Animal { // Dog가 Animal을 상속받음
      }

      6. 정리

    • 상속을 통해 코드 재사용성을 높일 수 있다.
    • 다형성을 활용하면 동일한 메서드를 다양한 방식으로 구현할 수 있다.
    • 상속과 다형성을 통해 애플리케이션의 유지보수성과 확장성을 향상시킬 수 있다.
    • 다음에 학습하면 좋은 주제: 인터페이스와 추상 클래스, 믹스인(Mixin) 사용법.