인터페이스와 추상 클래스
인터페이스와 추상 클래스는 객체지향 프로그래밍에서 중요한 두 가지 개념으로, 코드의 구조를 정의하고 재사용성과 유연성을 높이기 위해 사용됩니다. 이 두 개념은 클래스 간의 공통적인 동작을 정의하거나 다형성을 구현할 때 자주 사용되며, 둘 다 추상화를 통해 특정 동작을 강제합니다. 하지만, 이 둘의 사용 목적과 방법에는 차이가 있습니다.
인터페이스
개념
인터페이스Interface는 메서드의 시그니처(선언부)만 정의하고 구현은 제공하지 않는 추상적인 계약입니다. 인터페이스는 객체가 어떤 동작을 해야 하는지를 정의하며, 이 동작을 구체적으로 어떻게 구현할지는 해당 인터페이스를 구현하는 클래스에 위임합니다. 인터페이스는 다중 상속이 가능하여, 한 클래스가 여러 인터페이스를 구현할 수 있습니다. 이를 통해 클래스는 다양한 동작을 쉽게 추가할 수 있습니다.
인터페이스의 주요 특징
- 구현 강제: 인터페이스에 정의된 메서드를 구현하는 모든 클래스는 해당 메서드를 반드시 구현해야 합니다.
- 다중 상속 지원: 한 클래스는 여러 인터페이스를 구현할 수 있어 유연한 구조를 설계할 수 있습니다.
- 추상화 수준: 인터페이스는 구현 세부 사항을 숨기고, 동작의 선언만 제공하므로 추상화 수준이 높습니다.
- 상태를 가질 수 없음: 인터페이스는 필드를 가질 수 없으며, 순수하게 메서드 선언만 가능합니다.
인터페이스의 예시
public interface IBorrowable
{
void Borrow(string userId);
void Return(string userId);
}
public interface IReservable
{
void Reserve(string userId);
}
public class Book : IBorrowable, IReservable
{
public void Borrow(string userId)
=> Console.WriteLine($"{userId} borrowed the book.");
public void Return(string userId)
=> Console.WriteLine($"{userId} returned the book.");
public void Reserve(string userId)
=> Console.WriteLine($"{userId} reserved the book.");
}위 예시에서 Book 클래스는 IBorrowable과 IReservable이라는 두 인터페이스를 구현합니다. 이로 인해 Book은 대출, 반납, 예약 기능을 갖춘 객체로 설계되었습니다. 인터페이스 덕분에 Book 클래스는 유연하게 여러 역할을 수행할 수 있습니다.
언제 인터페이스를 사용할까?
- 다양한 클래스에 공통 동작을 강제하고 싶을 때
- 다중 상속이 필요할 때
- 특정 클래스의 구체적인 구현을 노출시키고 싶지 않을 때
추상 클래스
개념
추상 클래스Abstract Class는 구현이 일부 포함된 클래스로, 다른 클래스들이 상속받아 사용하도록 설계된 클래스입니다. 추상 클래스는 일부 메서드는 구현할 수 있지만, 일부 메서드는 구현하지 않고 자식 클래스가 반드시 구현하도록 강제할 수 있습니다. 추상 클래스는 상태Field를 가질 수 있으며, 상속을 통해 코드의 중복을 줄이고 기능을 확장할 수 있습니다. 추상 클래스는 인터페이스와 달리 단일 상속만 가능합니다. 즉, 한 클래스는 오직 하나의 추상 클래스만 상속받을 수 있습니다.
추상 클래스의 주요 특징
- 구현 제공 가능: 추상 클래스는 일부 메서드를 직접 구현할 수 있으며, 필요한 경우 자식 클래스에 해당 메서드의 구현을 강제할 수 있습니다.
- 단일 상속: 추상 클래스는 단일 상속만 지원합니다.
- 상태 유지 가능: 추상 클래스는 필드를 가질 수 있어 객체의 상태를 유지하거나 공유할 수 있습니다.
- 부분적 추상화: 추상 클래스는 인터페이스보다 구체적인 구현이 포함될 수 있으므로, 부분적인 추상화를 제공합니다.
추상 클래스의 예시
public abstract class LibraryItem
{
public string Title { get; set; }
public string Author { get; set; }
public abstract void DisplayInfo();
public virtual void Borrow(string userId)
=> Console.WriteLine($"{userId} borrowed {Title}.");
}
public class Book : LibraryItem
{
public string ISBN { get; set; }
public override void DisplayInfo()
=> Console.WriteLine($"Book: {Title}, Author: {Author}, ISBN: {ISBN}");
}
public class Magazine : LibraryItem
{
public int IssueNumber { get; set; }
public override void DisplayInfo()
=> Console.WriteLine($"Magazine: {Title}, Author: {Author}, Issue: {IssueNumber}");
}위 예시에서 LibraryItem은 추상 클래스이며, 책과 잡지와 같은 도서 항목의 공통 속성과 동작(대출 기능)을 제공합니다. Book과 Magazine 클래스는 각각 LibraryItem을 상속받아 DisplayInfo 메서드를 구현하고, 자신의 구체적인 정보를 출력합니다.
언제 추상 클래스를 사용할까?
- 공통된 상태나 동작을 상속받는 하위 클래스들이 있을 때
- 기본적인 구현을 제공하고, 하위 클래스가 특정 부분만 수정하거나 확장해야 할 때
- 다형성을 사용하여 부모 클래스의 기능을 재사용하고 확장할 때
인터페이스와 추상 클래스의 비교
| 구분 | 인터페이스Interface | 추상 클래스Abstract Class |
|---|---|---|
| 관계 | “행동behavior” 정의 | “is-a” 관계를 표현 |
| 다중 상속 | 가능 | 불가능 (단일 상속만 지원) |
| 구현 | 메서드 선언만 가능 | 일부 구현 가능 |
| 필드 | 필드 가질 수 없음 | 필드를 가질 수 있음 |
| 추상화 수준 | 완전한 추상화 | 부분적 추상화 |
| 사용 목적 | 클래스에 동작 강제 | 클래스의 공통 속성과 동작 상속 |
맺음말
인터페이스와 추상 클래스는 모두 추상화를 통해 객체지향 프로그래밍의 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 인터페이스는 행동을 강제하는 데 유용하며, 다중 상속이 가능하여 다양한 동작을 유연하게 추가할 수 있습니다. 반면에, 추상 클래스는 공통 속성이나 기본 동작을 상속받아 사용해야 할 때 유용하며, 단일 상속을 통해 공통된 상태를 유지할 수 있습니다. 도서관리 시스템과 같은 프로젝트에서는 이 두 개념을 적절히 조합하여 유연하고 재사용 가능한 클래스를 설계할 수 있습니다.