소소한 일상과 잡다한 정보

IT/Dart

Dart_4일차 : 클래스(Class)와 객체(Object) 기초

pandada 2025. 2. 20. 15:16
반응형

1. 클래스 ( Class )

 클래스는 객체(Object)를 만들기 위한 설계도다.

 객체는 클래스 인스턴스(Instance)로, 실제로 생성된 데이터 덩어리라고 보면 된다.

 ✔️ 기본 클래스 만들기

 Car 클래스를 새성하고, brand, speed 속성(변수)를 추가

 accelerate() 메서드로 속도 증가 , 객체 생성 후 메서드 호출

class Car {	//클래스 생성
  String brand = "BMW";
  int speed = 0;

  void accelerate() {	//메서드 생성
    speed += 10;
    print("$brand의 속도: $speed km/h");
  }
}

void main() {
  Car myCar = Car(); // 객체 생성
  myCar.accelerate(); // 메서드 호출
  myCar.accelerate(); // 속도 증가
}

 

2. 생성자 ( Constructor )

 클래스에 생성자(Constructor)를 추가하면 객체 생성 시 값을 초기화 할 수 있다.

 ✔️ 생성자 사용하기

 생성자로 속성 초기화, 객체 생성시 값을 전달하여 초기 속도 설정 가능

class Car {
  String brand;
  int speed;

  Car(this.brand, this.speed); // 생성자

  void accelerate() {
    speed += 10;
    print("$brand의 속도: $speed km/h");
  }
}

void main() {
  Car myCar = Car("Tesla", 50); // 객체 생성 + 초기값 설정
  myCar.accelerate();
}

 

3. Getter & Setter

 Getter : 객체의 속성을 가져오는 함수

 Setter : 객체의 속성을 변경하는 함수

 ✔️ Getter & Setter 사용

class Car {
  String brand;
  int _speed = 0; // private 변수, 외부에서 직접 접근 방지 (언더스코어 _ 사용)

  Car(this.brand);

  int get speed => _speed; // Getter
  set speed(int value) {
    if (value >= 0) {
      _speed = value; // Setter
    }
  }

  void accelerate() {
    _speed += 10;
    print("$brand의 속도: $_speed km/h");
  }
}

void main() {
  Car myCar = Car("Hyundai");
  myCar.accelerate();
  myCar.speed = 100; // Setter 사용
  print("현재 속도: ${myCar.speed} km/h"); // Getter 사용
}

 

4. 상속 ( Inheritance )

 상속(Inheritance)을 사용하면 기존 클래스의 기능을 재사용할 수 있다.

 ✔️ 상속하기

 extends 를 사용하고, 부모 클래스의 기능을 자식 클래스에서 그대로 사용 가능하다. 

class Vehicle {
  String brand;
  int speed = 0;

  Vehicle(this.brand);

  void accelerate() {
    speed += 10;
    print("$brand의 속도: $speed km/h");
  }
}

// Car 클래스가 Vehicle을 상속받음
class Car extends Vehicle {
  int fuel = 100;
  //자식 클래스 생성자에서 부모 클래스의 생성자를 호출
  Car(String brand) : super(brand);

  void refuel() {
    fuel = 100;
    print("$brand의 연료가 가득 찼습니다.");
  }
}

void main() {
  Car myCar = Car("Toyota");
  myCar.accelerate(); // 부모 클래스의 메서드 사용 가능
  myCar.refuel(); // 자식 클래스의 메서드 사용 가능
}

 Car(String brand) : super(brand);는 부모 클래스의 생성자(Vehicle)을 호출하는 역할은 한다.

 즉, 자식 클래스(Car)가 생성될 때, 부모 클래스(Vehicle)의 생성자를 실행해서 초기값을 설정하는 내용이다. 

 

  • Car("Toyota")를 호출하면
    Car 생성자가 실행됨
  • Car(String brand) : super(brand); 실행
    super(brand);를 통해 부모 클래스(Vehicle)의 생성자인 Vehicle(this.brand); 실행
  • Vehicle 클래스의 brand가 "Toyota"로 설정됨
  • 즉, Car 객체가 생성되면서 brand 값이 부모 클래스에도 전달됨

 

 또한 super(brand); 없이 부모클래스의 속성을 초기화하지 않으면 오류가 발생할 수도 있다.

 

5. 인터페이스 ( Interface ) & 추상 클래스 ( Abstract Class )

 인터페이스(Interface)는 특정 기능을 강제하도록 만들어진 틀이며, 추상 클래스(Abstract Class)는 상속을 위한 기본 구조를 제공하는 클래스다.

 ✔️ 추상 클래스 사용하기

 abstract class 로 추상클래스를 선언하고, 보통 구현 없이 선언만 한다. 그리고 이후 자식 클래스에서 @override를 통해 새로운 기능을 구현한다.

abstract class Animal {
  void makeSound(); // 추상 메서드 (구현 없이 선언만 함)
}

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

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

void main() {
  Dog dog = Dog();
  dog.makeSound(); // "멍멍!" 출력
  Cat cat = Cat();
  cat.makeSound(); // "야옹!" 출력
}

  dog와 cat 객체를 생성하고, Animal 을 상속받아, 추상메서드를 override 하여 만든 내용.

 

 ✔️ 추상 클래스 (Abstract Class)를 사용하는 이유

 추상 클래스는 공통된 기능을 정의하고, 자식 클래스가 반드시 구현해야 할 메서드를 강제하기 위해 사용한다.
 즉, 일반 클래스처럼 직접 객체를 생성할 수 없고, 상속받는 클래스에서 구현해야 하는 틀을 제공하는 역할이다.

 

  • 공통 기능 정의 + 특정 기능 강제 (Dog, Cat이 makeSound()를 반드시 구현해야 함)
  • 객체 생성 방지 (Shape처럼 직접 인스턴스를 만들 수 없음)
  • 유지보수와 확장성을 향상 (Animal을 상속받으면 makeSound()를 구현해야 하므로 일관성 유지)

 

  아래 예제들을 통해서 내용을 이해하고 넘어가면 좋을 것 같다.

abstract class Animal {
  void makeSound(); // 추상 메서드 (반드시 구현해야 함)

  void eat() {
    print("음식을 먹습니다.");
  }
}

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

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

void main() {
  Dog dog = Dog();
  dog.makeSound(); // "멍멍!"
  dog.eat(); // "음식을 먹습니다."

  Cat cat = Cat();
  cat.makeSound(); // "야옹!"
  cat.eat(); // "음식을 먹습니다."
}
  • Animal 추상 클래스에서 makeSound()를 정의하고 구현은 안 함
  • 자식 클래스(Dog, Cat)는 makeSound()를 반드시 구현해야 함
  • 공통 기능(eat())은 추상 클래스에서 직접 구현 가능
      
abstract class Shape {
  double getArea(); // 추상 메서드 (각 도형마다 면적 계산이 다르므로 구현 없음)
}

class Circle extends Shape {
  double radius;

  Circle(this.radius);

  @override
  double getArea() {
    return 3.14 * radius * radius;
  }
}

void main() {
  // Shape shape = Shape(); ❌ 오류 발생 (추상 클래스는 직접 객체 생성 불가능)
  Circle circle = Circle(5);
  print("원의 면적: ${circle.getArea()}");
}
  • Shape 클래스는 getArea()라는 공통 메서드를 제공하지만, 직접 객체 생성은 불가능
  • 각 도형 클래스(Circle)에서 getArea()를 구현해야 함

 

반응형

 


✔️ 테스트 예제 코드 1

 위에서 진행했던 내용을 토대로 충분한 실습을 진행한 후, 테스트 예제를 진행해보도록 하자.

 1. Person 클래스를 만들고, name과 age 속성을 가진 객체를 생성하는 프로그램 만들기

 2. Getter & Setter를 활용하여, name과 age를 get으로 가져오고, set으로 age를 수정하게 만들기

 3. Setter를 활용할때에 나이 유효성 검사 추가하기 ( 음수 제외 )

 4. Person 클래스에 introduce() 메서드를 추가해서 이름과 나이를 출력.

class Person {
  String _name;
  int _age;

  // 생성자
  Person(this._name, this._age);

  // Getter
  String get name => _name;
  int get age => _age;

  // Setter (유효성 검사 포함)
  set age(int value) {
    if (value > 0) {
      _age = value;
    } else {
      print("⚠️ 나이는 0보다 커야 합니다.");
    }
  }

  // 소개하는 메서드
  void introduce() {
    print("안녕하세요! 저는 $_name, $_age살입니다.");
  }
}

void main() {
  Person p = Person("Kim", 12);
  print("${p.name}, ${p.age}"); // Getter 사용

  p.age = -5; // 나이 유효성 검사 (잘못된 값)
  p.age = 25; // 올바른 값 설정
  print("수정된 나이: ${p.age}");

  p.introduce(); // 메서드 호출
}

 

 

✔️ 테스트 예제 코드 2-1

 1. BankAccount 클래스를 만들고, deposit(), withdraw() 메서드를 구현하여 입출금을 처리하는 프로그램 만들기

 2. BankAccount 클래스에는 accountNumber(계좌번호)와 balance(잔액)이 있다.

 3. deposit(amount) -> 입금 / withdraw(amount) -> 출금 ( 잔액보다 많이 출금할 경우 예외 처리 )

import 'dart:io';

class BankAccount {
  String accountNumber;
  double _balance;

  // 생성자
  BankAccount(this.accountNumber, this._balance);

  // Getter (잔액 조회)
  double get balance => _balance;

  // 입금 메서드
  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
      print("💰 $amount원이 입금되었습니다. 현재 잔액: $_balance원");
    } else {
      print("⚠️ 입금 금액은 0보다 커야 합니다.");
    }
  }

  // 출금 메서드
  void withdraw(double amount) {
    if (amount > _balance) {
      print("🚨 출금 실패! 잔액이 부족합니다.");
    } else if (amount > 0) {
      _balance -= amount;
      print("💸 $amount원이 출금되었습니다. 현재 잔액: $_balance원");
    } else {
      print("⚠️ 출금 금액은 0보다 커야 합니다.");
    }
  }
}

void main() {
  // 계좌 생성
  BankAccount myAccount = BankAccount("123-456-789", 10000);

  print("계좌번호: ${myAccount.accountNumber}");
  print("현재 잔액: ${myAccount.balance}원");

  myAccount.deposit(5000);  // 정상 입금
  myAccount.withdraw(3000); // 정상 출금
  myAccount.withdraw(20000); // 잔액 부족 예외 처리
  myAccount.withdraw(-1000); // 잘못된 금액 예외 처리
}

 

✔️ 테스트 예제 코드 2-2

 1. 2-1의 작성한 코드를 토대로 여 계좌를 생성할 수 있도록 List<BankAccount>를 활용해보자( 3일차 내용 )

 2. 사용자로부터 계좌 정보를 입력받아 저장 ( stdin 사용 )

 3. 계좌 검색 기능 추가 ( 계좌번호로 특정 계좌 찾기 / 3일차 내용 )

 4. 입출금 기능은 그대로 유지

import 'dart:io';

class BankAccount {
  String accountNumber;
  double _balance;

  // 생성자
  BankAccount(this.accountNumber, this._balance);

  // Getter (잔액 조회)
  double get balance => _balance;

  // 입금 메서드
  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
      print("💰 $amount원이 입금되었습니다. 현재 잔액: $_balance원");
    } else {
      print("⚠️ 입금 금액은 0보다 커야 합니다.");
    }
  }

  // 출금 메서드
  void withdraw(double amount) {
    if (amount > _balance) {
      print("🚨 출금 실패! 잔액이 부족합니다.");
    } else if (amount > 0) {
      _balance -= amount;
      print("💸 $amount원이 출금되었습니다. 현재 잔액: $_balance원");
    } else {
      print("⚠️ 출금 금액은 0보다 커야 합니다.");
    }
  }
}

// 계좌 목록 저장
List<BankAccount> accounts = [];

// 사용자 입력 함수 (공백 방지)
String getUserInput(String prompt) {
  while (true) {
    stdout.write(prompt);
    String? input = stdin.readLineSync()?.trim();
    if (input != null && input.isNotEmpty) {
      return input;
    }
    print("⚠️ 올바른 값을 입력하세요.");
  }
}

// 숫자 입력 함수 (예외 처리)
double getAmountInput(String prompt) {
  while (true) {
    stdout.write(prompt);
    String? input = stdin.readLineSync();

    try {
      double amount = double.parse(input!);
      if (amount >= 0) {
        return amount;
      }
      print("⚠️ 금액은 0 이상이어야 합니다.");
    } catch (e) {
      print("⚠️ 올바른 숫자를 입력하세요.");
    }
  }
}

// 계좌 검색 함수
BankAccount? findAccount(String accountNumber) {
  for (var account in accounts) {
    if (account.accountNumber == accountNumber) {
      return account;
    }
  }
  return null;
}

void main() {
  print("📝 은행 계좌 관리 시스템");

  // 계좌 등록
  while (true) {
    String accountNumber = getUserInput("📌 새 계좌번호를 입력하세요 (종료: q): ");
    if (accountNumber == "q") break;

    double initialBalance = getAmountInput("💰 초기 잔액을 입력하세요: ");
    accounts.add(BankAccount(accountNumber, initialBalance));
    print("✅ 계좌가 등록되었습니다! (계좌번호: $accountNumber, 잔액: ${initialBalance}원)");
  }

  // 계좌 검색 및 입출금 기능
  while (true) {
    String searchAccount = getUserInput("🔍 계좌번호를 입력하세요 (종료: q): ");
    if (searchAccount == "q") break;

    BankAccount? account = findAccount(searchAccount);
    if (account == null) {
      print("🚨 해당 계좌를 찾을 수 없습니다.");
      continue;
    }

    while (true) {
      print("\n💳 [계좌: ${account.accountNumber}] 현재 잔액: ${account.balance}원");
      print("1. 입금  2. 출금  3. 종료");
      String choice = getUserInput("👉 원하는 작업을 선택하세요: ");

      if (choice == "1") {
        double depositAmount = getAmountInput("💰 입금할 금액을 입력하세요: ");
        account.deposit(depositAmount);
      } else if (choice == "2") {
        double withdrawAmount = getAmountInput("💸 출금할 금액을 입력하세요: ");
        account.withdraw(withdrawAmount);
      } else if (choice == "3") {
        break;
      } else {
        print("⚠️ 올바른 선택을 해주세요.");
      }
    }
  }

  print("📌 프로그램을 종료합니다.");
}

 

✔️ 테스트 예제 코드 3

 1. Shape 추상 클래스를 만들고, Circle과 Rectangle, Triangle 클래스를 추가하여 면적을 계산할 수 있도록 하는 프로그램

 2. 1을 입력받을 경우 원 면적 계산, 2를 입력받을 경우 사각형 면적 계산, 3을 입력받을 경우 삼각형 면적 계산

 3. Shape 추상 클래스 생성. 해당 추상 클래스는 getArea() 메서드를 가지고 있다.

 4. Circle 클래스 : radius(반지름) , getArea()  / d원 면적 계산 -> 3.14 * radius * radius

 5. Rectangle 클래스 : width(가로) , height(세로), getArea() 

 6. Triangle  클래스 : base(밑변) , height(높이),  getArea()  / 삼각형 면적 계산 -> 0.5 * base * height

 

import 'dart:io';

// 추상 클래스
abstract class Shape {
  double getArea(); // 면적 계산 메서드 (구현 없음)
}

// 원 클래스
class Circle extends Shape {
  double radius;

  Circle(this.radius);

  @override
  double getArea() {
    return 3.14 * radius * radius;
  }
}

// 사각형 클래스
class Rectangle extends Shape {
  double width, height;

  Rectangle(this.width, this.height);

  @override
  double getArea() {
    return width * height;
  }
}

// 삼각형 클래스
class Triangle extends Shape {
  double base, height;

  Triangle(this.base, this.height);

  @override
  double getArea() {
    return 0.5 * base * height;
  }
}

// 숫자 입력 함수 (공백 및 예외 처리)
double getNumberInput(String prompt) {
  while (true) {
    stdout.write(prompt);
    String? input = stdin.readLineSync();

    try {
      double number = double.parse(input!);
      if (number > 0) {
        return number;
      }
      print("⚠️ 숫자는 0보다 커야 합니다.");
    } catch (e) {
      print("⚠️ 올바른 숫자를 입력하세요.");
    }
  }
}

void main() {
  print("📝 도형 면적 계산기");

  while (true) {
    print("\n1. 원  2. 사각형  3. 삼각형  4. 종료");
    stdout.write("👉 원하는 도형을 선택하세요: ");
    String? choice = stdin.readLineSync();

    if (choice == "1") {
      double radius = getNumberInput("🟢 원의 반지름을 입력하세요: ");
      Circle circle = Circle(radius);
      print("✅ 원의 면적: ${circle.getArea()}");

    } else if (choice == "2") {
      double width = getNumberInput("🟦 사각형의 가로 길이를 입력하세요: ");
      double height = getNumberInput("🟦 사각형의 세로 길이를 입력하세요: ");
      Rectangle rectangle = Rectangle(width, height);
      print("✅ 사각형의 면적: ${rectangle.getArea()}");

    } else if (choice == "3") {
      double base = getNumberInput("🔺 삼각형의 밑변을 입력하세요: ");
      double height = getNumberInput("🔺 삼각형의 높이를 입력하세요: ");
      Triangle triangle = Triangle(base, height);
      print("✅ 삼각형의 면적: ${triangle.getArea()}");

    } else if (choice == "4") {
      print("📌 프로그램을 종료합니다.");
      break;

    } else {
      print("⚠️ 올바른 선택을 해주세요.");
    }
  }
}

 


 이렇게 클래스와 객체 기초 부분에 대해서 알아보았다. 추가 적인 내용이 필요한 경우에는 댓글을 요청드리고, 틀린 부분이 있다면 이것 또한 댓글로 알려주시면 수정하도록 하겠습니다!


 

반응형