인터페이스와 추상 클래스
인터페이스와 추상 클래스는 객체지향 프로그래밍에서 중요한 두 가지 개념으로, 코드의 구조를 정의하고 재사용성과 유연성을 높이기 위해 사용됩니다. 이 두 개념은 클래스 간의 공통적인 동작을 정의하거나 다형성을 구현할 때 자주 사용되며, 둘 다 추상화를 통해 특정 동작을 강제합니다. 하지만, 이 둘의 사용 목적과 방법에는 차이가 있습니다.
인터페이스
개념
인터페이스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” 관계를 표현 |
다중 상속 | 가능 | 불가능 (단일 상속만 지원) |
구현 | 메서드 선언만 가능 | 일부 구현 가능 |
필드 | 필드 가질 수 없음 | 필드를 가질 수 있음 |
추상화 수준 | 완전한 추상화 | 부분적 추상화 |
사용 목적 | 클래스에 동작 강제 | 클래스의 공통 속성과 동작 상속 |
맺음말
인터페이스와 추상 클래스는 모두 추상화를 통해 객체지향 프로그래밍의 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 인터페이스는 행동을 강제하는 데 유용하며, 다중 상속이 가능하여 다양한 동작을 유연하게 추가할 수 있습니다. 반면에, 추상 클래스는 공통 속성이나 기본 동작을 상속받아 사용해야 할 때 유용하며, 단일 상속을 통해 공통된 상태를 유지할 수 있습니다. 도서관리 시스템과 같은 프로젝트에서는 이 두 개념을 적절히 조합하여 유연하고 재사용 가능한 클래스를 설계할 수 있습니다.