[디자인 패턴] Adapter Pattern

전성훈·2023년 11월 8일
0

DesignPattern

목록 보기
16/18
post-thumbnail

주제: 인터페이스 호환성 문제 해결


클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴으로, 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동하도록 해준다.

어뎁터 패턴이란?

  • 서로 다른 인터페이스를 가진 두 클래스를 함께 사용할 수 있게 해주는 구조적 디자인 패턴이다. 이 패턴은 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.
  • Adapter 패턴을 사용하면 인터페이스 호환성 문제 때문에 함께 작동할 수 없는 클래스들을 연결해서 작동하게 할 수 있다.

어뎁터 패턴의 구조

  • Client
    • 기존 비즈니스 로직을 포함하는 클래스
  • Target Interface
    • Client 코드에 드러날 Adapter의 기능
  • Adaptee
    • Client와 호환되지 않는 인터페이스인 직접 사용할 수 없는 클래스, 이미 존재하는 인터페이스를 가지고 있는 클래스
  • Adapter
    • Client와 Adapter 모두와 함께 일할 수 있는 클래스, Adaptee 객체를 wrapping해서 Client 인터페이스를 구현

장단점

장점

  • 큰 유연성
    • 이미 존재하는 클래스의 인터페이스를 변경하지 않고도 그 클래스를 사용할 수 있개 해준다.
  • 코드 재사용
    • 기존의 코드를 재사용하면서 새로운 인터페이스 요구사항을 충족시킬 수 있다.

단점

  • 복잡성 증가
    • 전체 코드 복잡성이 증가할 수 있다. 새로운 인터페이스를 추가하는 것은 항상 코드 복잡성을 증가시킨다.
    • Adaptee를 extension하거나 변경하는게 더 간단할 수 있다.

결론

  • Adaptee의 기능과 Adapter의 동작을 비즈니스 로직과 분리 할 수 있기 때문에 SRP 단일 책임 원칙을 지킬 수 있다.
  • 어댑터를 통해 작업하면 Client 인터페이스를 통해서만 작업할 수 있고, 새로운 어댑터를 추가할 수 있기 때문에 OCP 개방/폐쇄 원칙을 지킬 수 있다.

예시 코드

기본 예시

// Target 
protocol NewCharging { 
	func charge()
}

// Adaptee
class OldChargingImplementation { 
	func specificCharge() { 
		print("Charging with old charger")
	}
}

// Adapter 
class ChargerAdapter: NewCharging { 
	var oldCharger: OldChargingImplementation 
	
	init(oldCharger: OldChargingImplementation) { 
		self.oldCharger = oldCharger 
	}
	
	func charge() { 
		oldCharger.specificCharge()
	}
}

// Client 
let oldCharger = OldChargingImplementation()
let adapter = ChargerAdapter(oldCharger: oldCharger)
adapter.charge()
  • 위 코드에서 NewCharging은 Target, OldChargingImplementation은 Adaptee, ChargerAdapter는 Adapter 역할을 합니다. 클라이언트는 NewCharging 인터페이스를 통해 충전을 수행하려고 합니다.
  • 하지만 현재 가지고 있는 충전기는 OldChargingImplementation으로, specificCharge라는 다른 메서드를 통해 충전을 수행합니다. 이 때 ChargerAdapter를 사용하면 OldChargingImplementationspecificCharge 메서드를 NewChargingcharge 메서드로 변환하여 클라이언트가 사용할 수 있게 해줍니다.

라이브러리 인터페이스 예시

  • 서로 다른 라이브러리나 프레임워크를 사용하려는데 그들의 인터페이스가 서로 다른 경우, Adapter 패턴을 사용해서 이를 해결할 수 있습니다.
  • 또는 기존에 사용하던 API를 새로운 API로 교체하려는 데, 이 두 API의 인터페이스가 서로 다른 경우에도 Adapter 패턴을 활용할 수 있습니다.
// Target 
protocol NewJsonParser { 
	func parse(data: Data) -> [String: Any]
}

// Adaptee 
class OldJsonParser { 
	func specificParse(from data: Data) -> [String: Any]? { 
		// Parsing Logic 
		return nil 
	}
}

// Adapter 
class JsonParserAdapter: NewJsonParser { 
	var oldParser: OldJsonParser
	
	init(oldParser: OldJsonParser) { 
		self.oldParser = oldParser
	} 
	
	func parse(data: Data) -> [String: Any?] { 
		return oldParser.specificParse(from: data)
	}
}

// Clinet 
let oldParser = OldJsonParser() 
let adapter = JsonParserAdapter(oldParser: oldParser)
let jsonData = Data()
let result = adapter.parse(data: jsonData)

출처(참고문헌)

제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.

감사합니다

0개의 댓글