핵심 차이점
1. Python (덕 타이핑) - 너무 자유로움
# 문제: 어떤 메서드가 필요한지 알기 어려움
def program(logic):
logic.process(data) # process 메서드가 있기를 '기대'만 함
class Logic:
def process(self, data):
return "처리됨"
class WrongLogic:
def handle(self, data): # process가 아니라 handle!
return "처리됨"
program(Logic()) # 작동
program(WrongLogic()) # 런타임 에러! AttributeError
어떤 메서드가 필요한지 코드만 봐서는 알 수 없음.
2. Java (명시적 구현) - 너무 경직됨
// 인터페이스 먼저 정의해야 함
public interface Logic {
String process(String data);
}
// 반드시 implements 키워드로 명시적 구현
public class LogicImpl implements Logic {
public String process(String data) {
return "처리됨";
}
}
// 새로운 요구사항: 다른 라이브러리의 클래스 사용하고 싶음
public class ThirdPartyProcessor {
public String handle(String data) { // 메서드명이 다름!
return "처리됨";
}
}
// 문제: ThirdPartyProcessor는 Logic을 구현하지 않음
// 해결책: 어댑터 패턴으로 감싸야 함
public class ProcessorAdapter implements Logic {
private ThirdPartyProcessor processor;
public String process(String data) {
return processor.handle(data); // 어댑터 코드 필요
}
}
기존 코드를 인터페이스에 맞추려면 추가 코드가 필요.
3. Go (암묵적 구현)
// 인터페이스 정의 (보통 사용하는 쪽에서 정의)
type Logic interface {
Process(data string) string
}
// 구현체 1: implements 키워드 없이 그냥 메서드만 구현
type LogicProvider struct{}
func (lp LogicProvider) Process(data string) string {
return "LogicProvider로 처리됨"
}
// 구현체 2: 다른 패키지의 기존 타입도 자동으로 호환
type ThirdPartyProcessor struct{}
func (tp ThirdPartyProcessor) Process(data string) string {
return "ThirdParty로 처리됨"
}
// 클라이언트 코드
type Client struct {
L Logic
}
func (c Client) Program(data string) string {
return c.L.Process(data)
}
func main() {
// 둘 다 자동으로 Logic 인터페이스를 만족
client1 := Client{L: LogicProvider{}}
client2 := Client{L: ThirdPartyProcessor{}}
fmt.Println(client1.Program("test"))
fmt.Println(client2.Program("test"))
}
실제 상황
상황: 기존 코드에 새로운 인터페이스 추가
// 1단계: 기존에 있던 구조체들
type FileLogger struct{}
func (f FileLogger) Write(msg string) { /* 파일에 쓰기 */ }
type ConsoleLogger struct{}
func (c ConsoleLogger) Write(msg string) { /* 콘솔에 쓰기 */ }
type DatabaseLogger struct{}
func (d DatabaseLogger) Write(msg string) { /* DB에 쓰기 */ }
// 2단계: 나중에 인터페이스가 필요해짐
type Logger interface {
Write(msg string)
}
// 3단계: 기존 코드 수정 없이 바로 사용 가능!
func logMessage(logger Logger, msg string) {
logger.Write(msg)
}
func main() {
// 모든 기존 타입이 자동으로 Logger 인터페이스를 만족
logMessage(FileLogger{}, "파일 로그")
logMessage(ConsoleLogger{}, "콘솔 로그")
logMessage(DatabaseLogger{}, "DB 로그")
}
Java라면: 모든 기존 클래스에 implements Logger를 추가필요.
Python이라면: 런타임에 Write 메서드가 없으면 에러.
Go라면: 기존 코드 수정 없이 바로 작동
외부 라이브러리 활용
// 외부 라이브러리의 HTTP 클라이언트
type HTTPClient struct{}
func (h HTTPClient) Get(url string) string { return "response" }
// 우리가 만든 HTTP 클라이언트
type MyHTTPClient struct{}
func (m MyHTTPClient) Get(url string) string { return "my response" }
// 나중에 필요에 의해 인터페이스 정의
type Getter interface {
Get(url string) string
}
// 둘 다 자동으로 Getter를 만족
func fetchData(client Getter, url string) string {
return client.Get(url)
}
func main() {
// 외부 라이브러리와 내부 구현 모두 호환
fetchData(HTTPClient{}, "http://api.com")
fetchData(MyHTTPClient{}, "http://api.com")
}