
자바에서 추상 클래스와 인터페이스를 배우는 중에 다음과 같이 생각했었다.
처음에는 단순히
정도로 이해했지만,
조금 더 고민해보니 이건 단순 문법이 아니라 설계 철학에 가까운 개념이었다.
abstract class Payment {
abstract void pay();
}
추상 메서드는 몸체가 없는 메서드다.
class CardPayment extends Payment {
@Override
void pay() {
System.out.println("카드 결제");
}
}
만약 pay()를 구현하지 않으면?
👉 컴파일 에러 발생
즉,
“이 메서드는 반드시 구현해야 한다”
를 개발자에게 강제하는 장치다.
인터페이스는 “기능의 규약(계약)”이다.
interface Payment {
void pay();
void cancel();
void refund();
}
구현 클래스는 implements로 연결한다.
class CardPayment implements Payment {
@Override
public void pay() { }
@Override
public void cancel() { }
@Override
public void refund() { }
}
인터페이스의 모든 메서드를 구현하지 않으면 역시 컴파일 에러가 발생한다.
만약 특정 결제 기능에 대한 매서드 이름이 다음처럼 제각각이라면:
사용하는 쪽에서는 혼란이 생긴다.
하지만 인터페이스로 pay()를 강제하면
모든 결제 객체는 같은 이름의 메서드를 가진다
이건 유지보수 관점에서 매우 큰 장점이다.
이게 인터페이스를 이용할 때 얻는 강점이다.
문제는 이런 경우다:
그래서 등장한 것이 어댑터 패턴이다.
abstract class PaymentAdapter implements Payment {
@Override
public void pay() {}
@Override
public void cancel() {}
@Override
public void refund() {}
}
이제 필요한 메서드만 오버라이딩하면 된다.
class CardPayment extends PaymentAdapter {
@Override
public void pay() {
System.out.println("카드 결제");
}
}
어댑터에 “빈 메서드”를 두면
실수로 호출해도 아무 일도 안 일어날 수 있다.
이건 버그를 숨길 위험이 있다.
그래서 더 안전한 방법은:
@Override
public void refund() {
throw new UnsupportedOperationException("지원하지 않음");
}
👉 호출되면 즉시 실패(Fail Fast)
인터페이스를 사용 시 좀 더 응용을 해서 템플릿 구조를 쓰면 이런 형태가 된다.
abstract class PaymentAdapter implements Payment {
@Override
public final void pay(int amount) {
validate(amount);
doPay(amount);
log();
}
protected abstract void doPay(int amount);
protected void validate(int amount) {}
protected void log() {}
}
여기서
pay()는 외부에서 호출하는 공식 입구 (public + final)doPay()는 자식만 구현 가능한 확장 지점 (protected)이 구조는:
외부는 흐름을 건드리지 못하고
자식은 필요한 부분만 구현할 수 있게 한다
이게 바로 Template Method 패턴이다.
| 구분 | 추상 메서드 | 예외 던지기 |
|---|---|---|
| 에러 시점 | 컴파일 시점 | 실행 시점 |
| 의미 | 반드시 구현 | 지원하지 않음 |
추상 메서드는 “사전 차단”
예외는 “사후 차단”이다.
환불이 없는 결제 수단이 있다면?
interface Payable { void pay(); }
interface Refundable { void refund(); }
필요한 기능만 구현하면 된다.
이게 인터페이스 분리 원칙(ISP)이다.
처음에는
“왜 이렇게 복잡하게 만들지?”
라고 생각했지만,
지금은 이렇게 느껴진다.
결국 이 모든 것은
“코드를 안전하게 확장하기 위한 장치”
라는 걸 알게 되었다.
추상 클래스와 인터페이스는
단순 문법이 아니라 설계 도구다.
이제는 문법을 넘어서,
를 고민해보는 단계로 넘어가는 것 같다.