인터페이스와 추상 클래스

인터페이스와 추상 클래스는 객체지향 프로그래밍에서 중요한 두 가지 개념으로, 코드의 구조를 정의하고 재사용성과 유연성을 높이기 위해 사용됩니다. 이 두 개념은 클래스 간의 공통적인 동작을 정의하거나 다형성을 구현할 때 자주 사용되며, 둘 다 추상화를 통해 특정 동작을 강제합니다. 하지만, 이 둘의 사용 목적과 방법에는 차이가 있습니다.

인터페이스

개념

인터페이스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 클래스는 IBorrowableIReservable이라는 두 인터페이스를 구현합니다. 이로 인해 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은 추상 클래스이며, 책과 잡지와 같은 도서 항목의 공통 속성과 동작(대출 기능)을 제공합니다. BookMagazine 클래스는 각각 LibraryItem을 상속받아 DisplayInfo 메서드를 구현하고, 자신의 구체적인 정보를 출력합니다.

언제 추상 클래스를 사용할까?

  • 공통된 상태나 동작을 상속받는 하위 클래스들이 있을 때
  • 기본적인 구현을 제공하고, 하위 클래스가 특정 부분만 수정하거나 확장해야 할 때
  • 다형성을 사용하여 부모 클래스의 기능을 재사용하고 확장할 때

인터페이스와 추상 클래스의 비교

구분인터페이스Interface추상 클래스Abstract Class
관계“행동behavior” 정의“is-a” 관계를 표현
다중 상속가능불가능 (단일 상속만 지원)
구현메서드 선언만 가능일부 구현 가능
필드필드 가질 수 없음필드를 가질 수 있음
추상화 수준완전한 추상화부분적 추상화
사용 목적클래스에 동작 강제클래스의 공통 속성과 동작 상속

맺음말

인터페이스와 추상 클래스는 모두 추상화를 통해 객체지향 프로그래밍의 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 인터페이스는 행동을 강제하는 데 유용하며, 다중 상속이 가능하여 다양한 동작을 유연하게 추가할 수 있습니다. 반면에, 추상 클래스는 공통 속성이나 기본 동작을 상속받아 사용해야 할 때 유용하며, 단일 상속을 통해 공통된 상태를 유지할 수 있습니다. 도서관리 시스템과 같은 프로젝트에서는 이 두 개념을 적절히 조합하여 유연하고 재사용 가능한 클래스를 설계할 수 있습니다.