반응형
1. Interface의 개요
1.1 Interface의 정의
Interface는 객체가 수행해야 할 행위(Method)의 명세(Contract) 를 정의하는 추상 타입입니다.
즉,
- 무엇을 할 것인가만 정의
- 어떻게 할 것인가는 구현 클래스가 결정
| 특징 | 설명 |
| 추상 타입 | 구현이 아닌 메소드 선언만 포함 |
| 다중 구현 가능 | 여러 클래스가 동일 Interface 구현 |
| 느슨한 결합 | 구현체 변경 시 영향 최소 |
| 다형성 지원 | 동일 인터페이스로 다양한 객체 처리 |
간단한 개념 예
interface Payment
pay()
class CardPayment implements Payment
class CashPayment implements Payment
// 실제 실행 시
Payment p = new CardPayment()
Payment p = new CashPayment()
→ 동일 Interface로 다양한 객체 처리
1.2 Interface UML 표현
UML에서는 «interface» 또는 lollipop notation 으로 표현합니다.

구현 관계

UML 관계 표기
| 관계 | UML 표기 | 의미 |
| Interface | «interface» | 인터페이스 정의 |
| Implementation | Realization | 클래스 구현 |
| Method | +method() | public method |
2. 개발 언어별 Interface 비교
2.1 언어별 Interface 특징
| 언어 | Interface 지원 | 특징 |
| C | 직접 없음 | Function Pointer로 구현 |
| C++ | 추상 클래스 | pure virtual function |
| Java | Interface 키워드 | 다중 구현 가능 |
| C# | Interface 키워드 | Java와 유사 |
| VB.NET | Interface 키워드 | Implements 사용 |
| Python | ABC / Duck typing | 추상 클래스 기반 |
2.2 Interface와 관련 Design Pattern
Interface는 대부분의 GoF Design Pattern의 핵심입니다.
| Pattern | 목적 |
| Strategy | 알고리즘 교체 |
| Factory Method | 객체 생성 분리 |
| Adapter | 인터페이스 변환 |
| Proxy | 접근 제어 |
| Decorator | 기능 확장 |
2.3 Design Pattern
① Strategy Pattern
알고리즘을 Interface로 분리하여 동적으로 교체

예제 코드
// ① Strategy 인터페이스
public interface Strategy {
void execute();
}
// ② ConcreteStrategy A
public class StrategyA implements Strategy {
@Override
public void execute() {
System.out.println("StrategyA 실행: 알고리즘 A 수행");
}
}
// ③ ConcreteStrategy B
public class StrategyB implements Strategy {
@Override
public void execute() {
System.out.println("StrategyB 실행: 알고리즘 B 수행");
}
}
// ④ Context — Strategy를 주입받아 위임
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute(); // 실제 알고리즘은 Strategy에 위임
}
}
// ⑤ Client
public class Main {
public static void main(String[] args) {
Context context = new Context(new StrategyA());
context.executeStrategy(); // StrategyA 실행: 알고리즘 A 수행
// 런타임에 전략 교체
context.setStrategy(new StrategyB());
context.executeStrategy(); // StrategyB 실행: 알고리즘 B 수행
}
}
코드 설명
Context는 Strategy 인터페이스에만 의존하기 때문에 StrategyA와 StrategyB를 전혀 알지 못합니다. 덕분에 새로운 전략(StrategyC)을 추가할 때 Context를 수정할 필요 없이 인터페이스만 구현하면 됩니다 — OCP(개방-폐쇄 원칙) 준수.
setStrategy()를 통해 런타임에 알고리즘을 동적으로 교체할 수 있는 것이 이 패턴의 핵심 장점입니다. 결제 수단 선택, 정렬 알고리즘 교체, 압축 방식 변경 등 "행동을 바꿔끼워야 하는" 상황에 적합합니다.
특징
- 알고리즘 교체 가능
- OCP(Open Closed Principle)
② Factory Method Pattern
객체 생성 로직을 Interface 기반으로 분리

예제 코드
// ① Product 인터페이스
public interface Product {
void use();
}
// ② ConcreteProduct — Product 구현체
public class ConcreteProduct implements Product {
private String name;
public ConcreteProduct(String name) {
this.name = name;
}
@Override
public void use() {
System.out.println(name + " 사용 중");
}
}
// ③ Creator — 팩토리 메서드를 선언하는 추상 클래스
public abstract class Creator {
// 팩토리 메서드: 서브클래스가 어떤 객체를 만들지 결정
public abstract Product createProduct();
// 공통 비즈니스 로직: 생성은 createProduct()에 위임
public void doSomething() {
Product product = createProduct();
System.out.print("Creator.doSomething → ");
product.use();
}
}
// ④ ConcreteCreator — 팩토리 메서드를 오버라이드해 구체 객체 반환
public class ConcreteCreator extends Creator {
@Override
public Product createProduct() {
return new ConcreteProduct("ConcreteProduct");
}
}
// ⑤ Client
public class Main {
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
// 팩토리 메서드 직접 호출
Product product = creator.createProduct();
product.use(); // ConcreteProduct 사용 중
// 또는 Creator의 템플릿 메서드를 통해 간접 호출
creator.doSomething(); // Creator.doSomething → ConcreteProduct 사용 중
}
}
실전 예시-알람
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
@Override public void send(String message) {
System.out.println("[EMAIL] " + message);
}
}
public class SmsNotification implements Notification {
@Override public void send(String message) {
System.out.println("[SMS] " + message);
}
}
public abstract class NotificationCreator {
public abstract Notification createNotification();
public void notify(String message) {
createNotification().send(message);
}
}
public class EmailCreator extends NotificationCreator {
@Override public Notification createNotification() { return new EmailNotification(); }
}
public class SmsCreator extends NotificationCreator {
@Override public Notification createNotification() { return new SmsNotification(); }
}
// Client
NotificationCreator creator = new EmailCreator();
creator.notify("주문이 완료되었습니다."); // [EMAIL] 주문이 완료되었습니다.
creator = new SmsCreator();
creator.notify("배송이 시작되었습니다."); // [SMS] 배송이 시작되었습니다.
③ Adapter Pattern
호환되지 않는 인터페이스 연결

예제 코드
// ① Target — Client가 기대하는 인터페이스
public interface Target {
void request();
}
// ② Adaptee — 이미 존재하는 클래스 (인터페이스가 맞지 않음)
public class Adaptee {
public void specificRequest() {
System.out.println("[Adaptee] specificRequest() 실행 (기존 방식)");
}
}
// ③ Adapter — Target을 구현하고, 내부에서 Adaptee에 위임
public class Adapter implements Target {
private final Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
System.out.println("[Adapter] request() 호출 → specificRequest()로 변환");
adaptee.specificRequest(); // 인터페이스 변환 후 위임
}
}
// ④ Client — Target만 바라보고, Adaptee의 존재를 모름
public class Client {
private final Target target;
public Client(Target target) {
this.target = target;
}
public void doWork() {
target.request();
}
}
// ⑤ Main
public class Main {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target adapter = new Adapter(adaptee);
Client client = new Client(adapter);
client.doWork();
// [Adapter] request() 호출 → specificRequest()로 변환
// [Adaptee] specificRequest() 실행 (기존 방식)
}
}
실전 예시-레거시 결제 시스템 연동
// 기존 레거시 결제 모듈 (변경 불가)
public class LegacyPaymentSystem {
public void makePayment(String accountNo, double amount) {
System.out.printf("[Legacy] 계좌 %s 에 %.0f원 결제%n", accountNo, amount);
}
}
// 신규 시스템이 기대하는 인터페이스
public interface PaymentProcessor {
void pay(String userId, int amount);
}
// 어댑터: 신규 인터페이스 → 레거시 호출로 변환
public class LegacyPaymentAdapter implements PaymentProcessor {
private final LegacyPaymentSystem legacy;
public LegacyPaymentAdapter(LegacyPaymentSystem legacy) {
this.legacy = legacy;
}
@Override
public void pay(String userId, int amount) {
String accountNo = "ACC-" + userId; // userId → 계좌번호 변환
legacy.makePayment(accountNo, amount); // 레거시에 위임
}
}
// Client (신규 시스템)
public class OrderService {
private final PaymentProcessor processor;
public OrderService(PaymentProcessor processor) {
this.processor = processor;
}
public void placeOrder(String userId, int price) {
System.out.println("주문 처리 중...");
processor.pay(userId, price);
}
}
// Main
PaymentProcessor adapter = new LegacyPaymentAdapter(new LegacyPaymentSystem());
OrderService service = new OrderService(adapter);
service.placeOrder("user42", 15000);
// 주문 처리 중...
// [Legacy] 계좌 ACC-user42 에 15000원 결제
3. 개발 언어별 Interface 예제
3.1 C 언어 (Function Pointer 기반)
C는 Interface가 없기 때문에 struct + function pointer 사용
#include <stdio.h>
typedef struct {
void (*pay)(int);
} Payment;
void cardPay(int amount) {
printf("Card Pay: %d\n", amount);
}
void cashPay(int amount) {
printf("Cash Pay: %d\n", amount);
}
int main() {
Payment card = { cardPay };
Payment cash = { cashPay };
card.pay(100);
cash.pay(200);
}
3.2 C++ Interface
C++에서는 pure virtual function 사용
#include <iostream>
using namespace std;
class Payment {
public:
virtual void pay(int amount) = 0;
};
class CardPayment : public Payment {
public:
void pay(int amount) {
cout << "Card Pay: " << amount << endl;
}
};
int main() {
Payment* p = new CardPayment();
p->pay(100);
}
3.3 Java Interface
interface Payment {
void pay(int amount);
}
class CardPayment implements Payment {
public void pay(int amount) {
System.out.println("Card Pay : " + amount);
}
}
public class Main {
public static void main(String[] args) {
Payment p = new CardPayment();
p.pay(100);
}
}
3.4 C# Interface
using System;
interface IPayment
{
void Pay(int amount);
}
class CardPayment : IPayment
{
public void Pay(int amount)
{
Console.WriteLine("Card Pay " + amount);
}
}
class Program
{
static void Main()
{
IPayment p = new CardPayment();
p.Pay(100);
}
}
3.5 VB.NET Interface
Interface IPayment
Sub Pay(amount As Integer)
End Interface
Class CardPayment
Implements IPayment
Public Sub Pay(amount As Integer) Implements IPayment.Pay
Console.WriteLine("Card Pay " & amount)
End Sub
End Class
Module Program
Sub Main()
Dim p As IPayment = New CardPayment()
p.Pay(100)
End Sub
End Module
3.6 Python Interface
Python은 ABC(Abstract Base Class) 사용
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount):
pass
class CardPayment(Payment):
def pay(self, amount):
print("Card Pay:", amount)
p = CardPayment()
p.pay(100)
4. Method Overload / Method Override
4.1 Method Overload의 정의
같은 이름의 메소드지만 파라미터가 다른 메소드
add(int a, int b)
add(int a, int b, int c)
4.2 Method Override의 정의
부모 클래스 메소드를 자식 클래스에서 재정의
class Parent
run()
class Child
run() ← override
4.3 Method Overload와 Method Override 비교
| 구분 | Overload | Override |
| 목적 | 메소드 확장 | 기능 재정의 |
| 위치 | 동일 클래스 | 상속 관계 |
| 파라미터 | 다름 | 동일 |
| 반환 타입 | 동일/다름 가능 | 동일 |
| 다형성 | compile time | runtime |
4.4 예제 코드 (Java)
Overload
class Calc {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
}
Override
class Animal {
void sound() {
System.out.println("Animal Sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog Bark");
}
}
OOP 핵심 구조

반응형
'개발 > OOP' 카테고리의 다른 글
| 객체지향(OOP)의 Class/Method/변수 및 일반화와 상속성 (0) | 2026.03.16 |
|---|