상속은 객체 지향 프로그래밍(OOP)에서 중요한 개념 중 하나로, 클래스들 간의 계층 구조를 형성하여 코드의 재사용성을 높이고 구조를 조직화하는 데 사용됩니다. 이를 통해 기존 클래스의 특징을 물려받고, 새로운 클래스를 만들어 확장할 수 있습니다.
상속의 개념
상속은 하위 클래스(자식 클래스)가 상위 클래스(부모 클래스)의 특성과 기능을 물려받는 것을 의미합니다. 부모 클래스의 모든 특성(멤버 변수, 메서드)은 자식 클래스에서 사용할 수 있으며, 이를 통해 코드의 중복을 줄이고 유사한 객체들을 쉽게 만들 수 있습니다.
상속의 구현
기본 문법
C#에서 상속은 :(콜론)을 이용하여 구현됩니다.
public class ParentClass
{
// 부모 클래스의 멤버 변수와 메서드
}
public class ChildClass : ParentClass
{
// 자식 클래스의 추가 멤버 변수와 메서드
}
위 코드에서 ChildClass는 ParentClass를 상속하고 있습니다.
상속의 종류
1. 단일 상속(Single Inheritance): C#은 단일 상속을 지원합니다. 즉, 하나의 클래스는 하나의 직계 부모 클래스만 상속할 수 있습니다.
2. 다중 상속(Multiple Inheritance): 한 클래스가 여러 개의 클래스를 동시에 상속하는 것을 말합니다. C#은 다중 상속을 직접 지원하지 않습니다.
접근 제한자와 상속
상속 관계에서는 멤버 변수와 메서드의 접근 제한자가 중요합니다. 부모 클래스의 private 멤버는 자식 클래스에서 직접 접근할 수 없으나, protected나 public 멤버는 자식 클래스에서 접근 가능합니다.
상속의 장점
코드의 재사용성
부모 클래스의 특성을 상속받아 새로운 클래스를 생성할 수 있기 때문에 코드의 중복을 최소화하고, 유사한 객체들을 쉽게 만들 수 있습니다.
계층 구조의 표현
클래스 간의 상속 관계를 통해 계층 구조를 형성하여 코드를 조직화하고 이해하기 쉽게 만듭니다.
확장성
부모 클래스를 수정하면 이를 상속받은 모든 자식 클래스에 영향을 줄 수 있습니다. 따라서 코드의 수정이 쉬우며, 새로운 기능을 추가하거나 변경할 수 있습니다.
상속의 주의점
의존성 문제
객체 간의 높은 의존성을 초래할 수 있습니다. 하위 클래스가 부모 클래스에 강하게 의존하면, 부모 클래스의 변경이 하위 클래스에도 영향을 줄 수 있습니다.
메서드 오버라이딩 주의
부모 클래스의 메서드를 자식 클래스에서 재정의(오버라이딩)할 때, 적절하게 사용해야 합니다. 부모 클래스의 메서드를 오버라이딩하면 그 기능을 변경하거나 확장할 수 있습니다.
다형성(Polymorphism)과 연관성
다형성은 상속과 밀접한 관련이 있습니다. 부모 클래스 타입의 변수로 자식 클래스의 객체를 참조할 수 있고, 이를 통해 같은 메서드 호출로 다양한 동작을 수행할 수 있습니다. 다형성은 코드의 유연성을 높이고, 객체 간의 결합도를 낮추어 프로그램을 확장하고 유지보수하기 쉽게 만듭니다.
가상 메서드(Virtual Methods)
C#에서는 부모 클래스의 메서드를 자식 클래스에서 재정의할 수 있도록 virtual 키워드를 사용합니다. 자식 클래스에서 이러한 가상 메서드를 override 키워드를 통해 오버라이드할 수 있습니다.
public class ParentClass
{
public virtual void MyMethod()
{
Console.WriteLine("Parent method");
}
}
public class ChildClass : ParentClass
{
public override void MyMethod()
{
Console.WriteLine("Child method");
}
}
다형성의 예시
ParentClass obj1 = new ParentClass();
obj1.MyMethod(); // 출력: "Parent method"
ParentClass obj2 = new ChildClass();
obj2.MyMethod(); // 출력: "Child method"
위 예시에서 obj1은 ParentClass의 인스턴스를 참조하고 있고, obj2는 ChildClass의 인스턴스를 참조하고 있습니다. 이때, MyMethod()가 호출되면 obj1은 ParentClass의 메서드를, obj2는 ChildClass에서 오버라이드한 메서드를 호출합니다.
상속과 인터페이스
인터페이스(interface)는 클래스의 특성을 정의하는데 사용됩니다. 클래스에서 다중 상속을 지원하지 않는 경우, 인터페이스를 사용하여 다양한 클래스가 동일한 행동을 수행할 수 있도록 해줍니다.
public interface IShape
{
void Draw();
}
public class Circle : IShape
{
public void Draw()
{
Console.WriteLine("Circle drawn");
}
}
public class Rectangle : IShape
{
public void Draw()
{
Console.WriteLine("Rectangle drawn");
}
}
위 코드에서 Circle과 Rectangle 클래스는 IShape 인터페이스를 구현하고 있습니다. 이를 통해 서로 다른 클래스가 동일한 인터페이스를 구현하여 동일한 메서드를 제공함으로써 다형성을 활용할 수 있습니다.
상속의 예제
예제 1: 동물 클래스와 그 하위 클래스들
using System;
// 부모 클래스
public class Animal
{
public void Eat()
{
Console.WriteLine("Eating...");
}
public void Sleep()
{
Console.WriteLine("Sleeping...");
}
}
// 자식 클래스 1
public class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Barking...");
}
}
// 자식 클래스 2
public class Cat : Animal
{
public void Meow()
{
Console.WriteLine("Meowing...");
}
}
위의 코드에서 Animal 클래스는 Eat()과 Sleep() 메서드를 가지고 있습니다. 이 클래스를 상속받아 Dog와 Cat 클래스를 만들었습니다. Dog 클래스는 Bark() 메서드를, Cat 클래스는 Meow() 메서드를 추가로 가지고 있습니다.
예제 2: 도형 클래스와 그 하위 클래스들
using System;
// 부모 클래스
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
// 자식 클래스 1
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
// 자식 클래스 2
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
위의 코드에서 Shape 클래스는 Draw() 메서드를 가지고 있습니다. Circle 클래스와 Rectangle 클래스는 각각 이 메서드를 오버라이드하여 도형을 그리는 방식을 변경했습니다.
예제 3: 계산기 클래스와 상속을 이용한 확장
using System;
// 부모 클래스
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public int Subtract(int a, int b)
{
return a - b;
}
}
// 자식 클래스
public class AdvancedCalculator : Calculator
{
public int Multiply(int a, int b)
{
return a * b;
}
public int Divide(int a, int b)
{
if (b != 0)
return a / b;
else
{
Console.WriteLine("Cannot divide by zero.");
return 0;
}
}
}
위의 코드에서 Calculator 클래스는 더하기와 빼기 기능을 가지고 있습니다. 이를 상속받아 AdvancedCalculator 클래스에서는 곱셈과 나눗셈 기능을 추가로 구현했습니다.
위 예제들은 상속을 통해 부모 클래스의 기능을 재사용하고 확장할 수 있다는 것을 보여줍니다.
상속을 통해 플레이어의 HP를 구현하고 사용하는 예제
using System;
// 부모 클래스
public class Character
{
protected int healthPoints; // HP
public Character(int initialHealth)
{
healthPoints = initialHealth;
}
public void TakeDamage(int damage)
{
healthPoints -= damage;
Console.WriteLine($"Took {damage} damage. Current HP: {healthPoints}");
}
public void Heal(int healAmount)
{
healthPoints += healAmount;
Console.WriteLine($"Healed {healAmount} points. Current HP: {healthPoints}");
}
public void DisplayHP()
{
Console.WriteLine($"Current HP: {healthPoints}");
}
}
// 자식 클래스 (플레이어)
public class Player : Character
{
public Player(int initialHealth) : base(initialHealth)
{
// 상속받은 생성자 호출
}
public void UseHealthPotion()
{
Heal(20); // 체력 포션 사용하여 20의 HP 회복
}
}
class Program
{
static void Main()
{
Player player = new Player(100); // 플레이어의 초기 HP는 100
player.DisplayHP(); // 현재 체력 표시
player.TakeDamage(30); // 플레이어가 30의 데미지를 입음
player.DisplayHP(); // 현재 체력 표시
player.UseHealthPotion(); // 플레이어가 체력 포션을 사용함
player.DisplayHP(); // 현재 체력 표시
}
}
위의 예제에서 Character 클래스는 모든 캐릭터의 공통적인 기능을 정의하며, Player 클래스는 Character 클래스를 상속하여 플레이어의 특수한 기능을 추가했습니다. Player 클래스는 UseHealthPotion() 메서드를 통해 체력 포션을 사용할 수 있습니다.
Main() 메서드에서는 Player 클래스의 인스턴스를 만들고, 체력을 표시하고 데미지를 입히며, 체력을 회복하는 등의 동작을 시뮬레이션하였습니다.
상속은 객체 지향 프로그래밍에서 중요한 개념으로, 코드의 재사용성을 높이고 계층 구조를 형성하여 유연하고 확장 가능한 프로그램을 작성하는 데 도움을 줍니다. 상속을 통해 부모 클래스의 특성을 재사용하고, 다형성을 통해 동일한 인터페이스를 가진 객체를 다양하게 처리할 수 있습니다.그러나 적절한 사용과 설계가 필요하며, 지나치게 복잡한 계층 구조는 코드를 이해하기 어렵게 할 수 있으므로 주의가 필요합니다.
'C#' 카테고리의 다른 글
[C#] 인터페이스 (0) | 2024.01.12 |
---|---|
[C#] 다형성 (0) | 2024.01.12 |
[C#] 캡슐화 (0) | 2023.12.23 |
[C#] 클래스와 객체 (0) | 2023.12.22 |
[C#] 람다함수 (0) | 2023.12.22 |