[Java] Abstract과 Interface의 차이
추상 클래스: 기본 구현을 제공하는 설계 틀
추상 클래스는 인스턴스화할 수 없지만, 공통된 필드나 메서드를 미리 구현해 두어 여러 하위 클래스들이 이를 상속받아 사용할 수 있도록 합니다.
이 방법은 중복 코드를 줄이고, 공통된 동작을 한 곳에서 관리할 수 있는 장점을 제공합니다.
동물 클래스 예시
동물이라는 개념을 예로 들어보면, 모든 동물이 공통으로 가지는 특성(예: 이름, 소리내기)을 추상 클래스로 정의할 수 있습니다.
다음 코드는 동물 클래스의 추상적 설계를 보여줍니다.
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 각 동물이 구현해야 하는 소리내기 메서드
public abstract void makeSound();
// 모든 동물이 공통적으로 사용할 수 있는 기능: 이름 출력
public void printName() {
System.out.println("동물의 이름: " + name);
}
}
이 추상 클래스를 상속받아 구체적인 동물 클래스(예: 개, 고양이 등)를 작성하면, 각 동물은 자신만의 소리내기 방식을 구현하면서도 공통 기능은 그대로 사용할 수 있습니다.
차량 클래스 예시
또 다른 예로, 다양한 종류의 차량을 다루는 시스템을 생각해 볼 수 있습니다.
차량의 공통 기능(예: 시동 걸기, 정지하기)을 추상 클래스로 구현하면, 각 차량 타입은 고유의 시동 방식만 구현하면 되므로 코드의 중복을 방지할 수 있습니다.
public abstract class Vehicle {
protected String model;
public Vehicle(String model) {
this.model = model;
}
// 각 차량이 구현해야 하는 시동 걸기 기능
public abstract void startEngine();
// 모든 차량에서 공통적으로 사용할 수 있는 정지 기능
public void stop() {
System.out.println(model + " 정지합니다.");
}
}
이처럼 추상 클래스를 활용하면, 공통된 기능은 한 곳에서 관리하고, 각 하위 클래스는 개별 특성만을 집중적으로 구현할 수 있습니다.
사용자 인터페이스(UI) 컴포넌트 예시
UI 프레임워크에서도 추상 클래스는 유용하게 사용됩니다
예를 들어, 기본적인 UI 컴포넌트의 공통 속성과 동작(위치 정보, 렌더링 기능 등)을 추상 클래스로 정의하면, 버튼, 텍스트 필드 등 다양한 컴포넌트가 이를 상속받아 개별적인 기능을 추가할 수 있습니다.
public abstract class UIComponent {
protected int x, y; // 컴포넌트의 위치 정보
public UIComponent(int x, int y) {
this.x = x;
this.y = y;
}
// 각 컴포넌트가 직접 구현해야 하는 화면 렌더링 메서드
public abstract void render();
// 공통적인 이동 기능
public void move(int deltaX, int deltaY) {
x += deltaX;
y += deltaY;
}
}
이런 설계를 통해 UI 개발 시 공통된 기능은 추상 클래스에서 관리하고, 각 컴포넌트는 고유한 렌더링 방식만 구현하면 되어 효율적인 개발이 가능합니다.
인터페이스: 규칙을 강제하는 약속
인터페이스는 클래스들이 반드시 지켜야 할 메서드의 시그니처만을 선언해 두어, 각 클래스가 동일한 방식으로 동작하도록 강제합니다.
즉, 인터페이스를 구현하는 클래스들은 정해진 메서드를 반드시 포함해야 하므로, 서로 다른 클래스 간에도 일관된 상호작용을 보장할 수 있습니다.
결제 시스템 예시
인터페이스는 클래스들 사이의 일관된 규칙을 강제하여, 다양한 구현체들이 동일한 방식으로 동작할 수 있도록 돕습니다.
예를 들어, 다양한 결제 수단을 지원하는 애플리케이션을 설계할 때, 모든 결제 방식에 대해 공통 규칙을 정의할 수 있습니다.
public interface Payment {
// 결제 금액을 처리하는 메서드
void processPayment(double amount);
// 결제 성공 여부를 반환하는 메서드
boolean isSuccessful();
}
이 인터페이스를 기반으로 신용카드, 계좌이체, 모바일 결제 등 각 결제 방식에 대한 구체적인 구현 클래스를 작성할 수 있습니다.
이처럼 인터페이스를 사용하면 여러 결제 방식 간에 동일한 약속을 유지할 수 있어, 시스템 확장이나 유지보수가 용이해집니다.
로그 기록 예시
또 다른 예로, 다양한 로그 기록 방식을 사용하는 시스템을 생각해볼 수 있습니다.
로그 메시지를 기록하는 기본 규칙을 인터페이스로 정의하면, 콘솔, 파일, 혹은 원격 서버에 로그를 남기는 방식에 상관없이 일관된 인터페이스를 제공할 수 있습니다.
public interface Logger {
// 로그 메시지를 기록하는 메서드
void log(String message);
}
이와 같이 인터페이스를 활용하면, 새로운 로그 방식을 추가할 때 기존 코드에 큰 영향을 주지 않고도 확장이 가능합니다.
마무리…
인터페이스와 추상 클래스는 각각의 목적에 따라 서로 다른 장점을 제공합니다.
실제 개발 현장에서는 이 두 가지 개념을 상황에 맞게 적절히 활용하는 것이 중요합니다.
각각의 특성을 잘 이해하고 적용한다면, 코드의 유지보수성, 확장성, 그리고 가독성을 모두 향상시킬 수 있을 것입니다.
이 글이 인터페이스와 추상 클래스의 개념을 실제 예시를 통해 이해하는 데 도움이 되기를 바랍니다.
Leave a comment