# Part 4: 객체 지향 프로그래밍 (OOP) 실습

# --- 클래스와 객체 ---
print("--- 클래스와 객체 ---")


class Animal:
    # 클래스 속성
    species = "동물"

    # 초기화 메서드 (생성자)
    def __init__(self, name, age):
        # 인스턴스 속성
        self.name = name
        self.age = age
        print(f"{self.name}(이)가 생성되었습니다.")

    # 인스턴스 메서드
    def speak(self):
        return "동물이 소리를 냅니다."

    def introduce(self):
        return f"저는 {self.name}이고, {self.age}살입니다."

    # 소멸자 메서드
    def __del__(self):
        print(f"{self.name}(이)가 소멸되었습니다.")


# 객체(인스턴스) 생성
my_dog = Animal("멍멍이", 3)

# 속성 접근
print(f"이름: {my_dog.name}")
print(f"나이: {my_dog.age}")
print(f"종: {Animal.species}")

# 메서드 호출
print(my_dog.introduce())
print(my_dog.speak())
print("-" * 20)


# --- 상속 (Inheritance) ---
print("\n--- 상속 ---")


# Animal 클래스를 상속받는 Dog 클래스
class Dog(Animal):
    # 메서드 오버라이딩 (Method Overriding)
    def speak(self):
        return "멍멍!"


# Animal 클래스를 상속받는 Cat 클래스
class Cat(Animal):
    def speak(self):
        return "야옹~"


# 자식 클래스의 인스턴스 생성
buddy = Dog("버디", 5)
lucy = Cat("루시", 2)

# 부모 클래스의 메서드와 오버라이딩된 메서드 호출
print(buddy.introduce())  # 부모 클래스의 메서드
print(f"{buddy.name}의 소리: {buddy.speak()}")  # 오버라이딩된 메서드

print(lucy.introduce())
print(f"{lucy.name}의 소리: {lucy.speak()}")
print("-" * 20)


# --- 다형성 (Polymorphism) ---
print("\n--- 다형성 ---")

animals = [Dog("레오", 4), Cat("나비", 1), Animal("알수없음", 0)]

for animal in animals:
    # 같은 animal.speak() 호출이지만,
    # 각 객체의 실제 클래스(Dog, Cat, Animal)에 따라 다른 결과가 나옴
    print(f"{animal.name}는 {animal.speak()} 하고 웁니다.")

print("-" * 20)

# del my_dog, buddy, lucy, animals # 수동으로 객체 소멸 (일반적으로는 불필요)
