Adapter는 호환되지 않는 interface가 있는 object가 공동 작얼을 할 수 있도록 하는 구조적 design pattern입니다.
주식시장 모니터링 앱을 만들고 있다고 상상해봅시다. 이 앱은 XML 형식의 여러 소스에서 주식 데이터를 다운로드 한 다음 사용자에게 멋진 차트와 다이어그램을 표시합니다.
어느 시점에서 타사 스마트 분석 라이브러리를 도입하여 개선하기로 결정하는데 문제가 있습니다. 해당 라이브러리는 JSON형식의 데이터로만 작동합니다.
XML과 함께 작동하도록 라이브러리를 변경할 수 있습니다. 그러나 이로 인해 라이브러리에 의존하는 기존 코드가 손상 될 수 있습니다. 더 나쁜 것은 처음부터 라이브러리의 소스 코드에 액세스 할 수 없어 이 접근 방식은 불가능합니다.
이 경우 adapter를 만들 수 있습니다. 이것은 다른 개체가 이해할 수 있도록 한 개체의 interface를 반환하는 특수 개체입니다.
Adapter는 개체 중 하나를 감싸서 장면 뒤에서 발생하는 변환의 복잡성을 숨 깁니다. Wrapping 된 개체는 어댑터도 인식하지 못합니다. 예를 들어 미터 및 킬로미터 단위로 작동하는 개체를 모든 데이터를 피트 및 마일과 같은 영국식 단위로 변환하는 어댑터를 사용하여 래핑 할 수 있습니다.
Adapter는 데이터를 다양한 형식으로 변환 할 수 있을뿐만 아니라 서로 다른 interface를 가진 객체가 협업하는데 도움을 줄 수 있습니다. 작동 방식은 다음과 같습니다.
때로는 양방향으로 통화를 변환 할 수 있는 양방향 adapter를 만드는 것도 가능합니다.
주식 시장 앱으로 돌아 갑시다. 호환되지 않는 형식의 딜레마를 해결하기 위해 코드가 직접 작동하는 분석 라이브러리의 모든 클래스에 대해 XML-JSON adapter를 만들 수 있습니다. 그런 다음 이러한 adapter를 통해서만 라이브러리와 통신하도록 코드를 조정합니다. Adapter가 호출을 받으면 들어오는 XML 데이터를 JSON 구조로 변환하고 wrapping 된 분석 개체의 적절한 메서드에 호출을 전달합니다.
한국에서 처음 캐나다로 여행을 가서 노트북을 충전하려고 할 때 놀라움을 느낄 수 있습니다. 전원 플러그 및 소켓 표준은 국가마다 다르기 때문에 한국용 충전기로는 충전을 할 수 없습니다. 이 경우 양국에서 모두 사용이 가능한 전원 플러그 adapter를 사용하여 문제를 해결할 수 있습니다.
이 구현에서는 개체 구성 원칙을 사용합니다. Adapter는 한 개체의 interface를 구현하고 다른 개체를 wrapping합니다. 널리 사용되는 모든 프로그래밍 언어로 구현할 수 있습니다.
이 구현에서는 상속을 사용합니다. Adapter는 동시에 두 개체의 interface를 상속합니다. 이 접근 방식은 C++와 같이 다중 상속을 지원하는 프로그래밍 언어로만 구현할 수 있습니다.
이 adapter pattern의 예는 사각 못과 둥근 구멍 사이의 고전적인 충돌을 기반으로 합니다.
Adapter는 사각형 지름의 절반에 해당하는 반지름(즉, 사각형 말뚝을 수용 할 수 있는 가장 작은 원의 반지름)을 가진 둥근 말뚝인 것처럼 가장합니다.
// Say you have two classes with compatible interfaces:
// RoundHole and RoundPeg.
class RoundHole is
constructor RoundHole(radius) { ... }
method getRadius() is
// Return the radius of the hole.
method fits(peg: RoundPeg) is
return this.getRadius() >= peg.getRadius()
class RoundPeg is
constructor RoundPeg(radius) { ... }
method getRadius() is
// Return the radius of the peg.
// But there's an incompatible class: SquarePeg.
class SquarePeg is
constructor SquarePeg(width) { ... }
method getWidth() is
// Return the square peg width.
// An adapter class lets you fit square pegs into round holes.
// It extends the RoundPeg class to let the adapter objects act
// as round pegs.
class SquarePegAdapter extends RoundPeg is
// In reality, the adapter contains an instance of the
// SquarePeg class.
private field peg: SquarePeg
constructor SquarePegAdapter(peg: SquarePeg) is
this.peg = peg
method getRadius() is
// The adapter pretends that it's a round peg with a
// radius that could fit the square peg that the adapter
// actually wraps.
return peg.getWidth() * Math.sqrt(2) / 2
// Somewhere in client code.
hole = new RoundHole(5)
rpeg = new RoundPeg(5)
hole.fits(rpeg) // true
small_sqpeg = new SquarePeg(5)
large_sqpeg = new SquarePeg(10)
hole.fits(small_sqpeg) // this won't compile (incompatible types)
small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
hole.fits(small_sqpeg_adapter) // true
hole.fits(large_sqpeg_adapter) // false
기존 class를 사용하려고 하지만 해당 interface가 나머지 코드와 호환되지 않는 경우 adapter class를 사용합니다.
Adapter pattern을 사용하면 코드와 레거시 class, 타사 class 또는 이상한 interface가 있는 다른 class 사이에서 변환기 역할을 하는 중간 계층 class를 만들 수 있습니다.
Superclass에 추가 할 수 없는 일부 공통 기능이 없는 기존의 여러 subclass를 재사용하려는 경우 pattern을 사용합니다.
각 subclass를 확장하고 누락 된 기능을 새 subclass에 넣을 수 있습니다. 그러나 이러한 모든 새 class에서 복제해야 하므로 냄새가 정말 안 좋습니다.
훨씬 더 우아한 해결책은 누락 된 기능을 adapter class에 넣는 것입니다. 그런 다음 adapter 내부에 누락 된 기능이 있는 개체를 wrapping하여 필요한 기능을 동적으로 얻습니다. 이 작업을 수행하려면 대상 class에 공통 interface가 있어야 하며 adapter 필드는 해당 interface를 따라야합니다. 이 접근 방식은 Decorator pattern과 매우 유사합니다.
O 단일 책임 원칙. 프로그램의 기본 비즈니스 로직에서 interface 또는 데이터 변환 코드를 분리 할 수 있습니다.
O 개방/폐쇄 원칙. Client interface를 통해 adapter와 함께 작동하는 한 기존 client 코드를 손상시키지 않고 새로운 유형의 adapter를 프로그램에 도입 할 수 있습니다.
X 새로운 interface 및 class 세트를 도입해야 하므로 코드의 전반적인 복잡성이 증가합니다. 때로는 나머지 코드와 일치하도록 service class를 변경하는 것이 더 간단합니다.
Bridge는 일반적으로 미리 설계되어 응용 프로그램의 일부를 서로 독립적으로 개발할 수 있습니다. 반면에 Adapter는 일반적으로 기존 앱과 함께 사용되어 호환되지 않는 일부 클래스가 잘 작동하도록합니다.
Adapter는 기존 객체의 인터페이스를 변경하는 반면 Decorator는 인터페이스를 변경하지 않고 객체를 향상시킵니다. 또한 Decorator는 Adapter를 사용할 때는 불가능한 재귀 적 구성을 지원합니다.
Adapter는 래핑 된 객체에 다른 인터페이스를 제공하고 Proxy는 동일한 인터페이스를 제공하며 Decorator는 향상된 인터페이스를 제공합니다.
Facade는 기존 개체에 대한 새 인터페이스를 정의하는 반면 Adapter는 기존 인터페이스를 사용 가능하게 만들려고합니다. 어댑터는 일반적으로 하나의 개체 만 래핑하는 반면 Facade는 개체의 전체 하위 시스템과 함께 작동합니다.
Bridge, State, Strategy (및 어느 정도 어댑터)는 매우 유사한 구조를 가지고 있습니다. 실제로 이러한 모든 패턴은 작업을 다른 개체에 위임하는 구성을 기반으로합니다. 그러나 그들은 모두 다른 문제를 해결합니다. 패턴은 특정 방식으로 코드를 구조화하기위한 단순한 방법이 아닙니다. 또한 패턴이 해결하는 문제를 다른 개발자에게 전달할 수 있습니다.