어댑터 패턴
Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 76번째.
어댑터 패턴(Adapter Pattern)은 호환되지 않는 인터페이스를 가진 객체들이 함께 작동할 수 있도록 하는 구조적 디자인 패턴입니다. 이는 두 인터페이스 사이의 브리지 역할을 하여, 호출을 한 형식에서 다른 형식으로 변환합니다.
결제를 처리하는 애플리케이션을 구축하고 있으며, 서드파티 결제 라이브러리를 통합해야 한다고 가정해 보겠습니다. 이 라이브러리는 완벽하게 작동하지만, 그 인터페이스가 여러분의 코드에서 기대하는 것과 일치하지 않습니다. 애플리케이션 전체를 다시 작성하는 대신, 두 인터페이스 사이를 변환해 주는 어댑터를 만듭니다.
<?php
// 애플리케이션이 기대하는 인터페이스
interface PaymentProcessor {
public function pay(float $amount): string;
}
// 다른 인터페이스를 가진 서드파티 라이브러리
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}
어댑터는 내부적으로 서드파티 클래스를 사용하면서 기대하는 인터페이스를 구현합니다:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
출력:
Charged 2999 USD via Stripe어댑터는 달러를 센트로 변환하고 적절한 메서드를 호출합니다. 여러분의 애플리케이션 코드는 PaymentProcessor만 알고 있으며, Stripe의 구체적인 요구 사항에 대해서는 전혀 알지 못합니다. 나중에 다른 결제 서비스 제공업체로 전환하더라도, 기존 코드를 변경하지 않고 단순히 새로운 어댑터를 생성하기만 하면 됩니다.
챌린지
쉬움어댑터 패턴(Adapter Pattern)을 사용하여 온도 변환 시스템을 구축해 보겠습니다. 화씨(Fahrenheit)로 판독값을 출력하는 레거시 온도 센서가 있지만, 현대적인 기상 애플리케이션은 모든 온도를 섭씨(Celsius)로 기대합니다. 두 시스템 중 어느 것도 수정하지 않고, 그 사이의 간극을 메워주는 어댑터를 만들 것입니다.
코드는 다음 네 개의 파일로 구성됩니다:
TemperatureSensor.php— 기상 애플리케이션이 기대하는TemperatureSensor인터페이스를 정의합니다. 이 인터페이스는 섭씨 온도를 반환하는getTemperature(): float메서드를 가져야 합니다.LegacySensor.php— 오래된 하드웨어를 시뮬레이션하는LegacySensor클래스를 생성합니다. 이 클래스는 애플리케이션에 필요한 것과 다른 인터페이스를 가집니다:- 생성자에서 화씨 판독값을 받습니다 (생성자 프로모션(constructor promotion)을 사용하세요).
- 저장된 화씨 값을 반환하는
readFahrenheit(): float메서드를 가집니다.
LegacySensorAdapter.php— TemperatureSensor 인터페이스와 LegacySensor 클래스를 모두 포함합니다.TemperatureSensor를 구현하고LegacySensor를 감싸는LegacySensorAdapter클래스를 생성합니다. 어댑터는 다음을 수행해야 합니다:- 생성자에서
LegacySensor객체를 받습니다. - 레거시 센서에서 화씨 값을 읽어와서
(fahrenheit - 32) * 5 / 9공식을 사용하여 섭씨로 변환한 뒤getTemperature()를 구현합니다.
- 생성자에서
main.php— LegacySensorAdapter 파일을 포함합니다. 화씨 온도라는 하나의 입력을 받게 됩니다.입력받은 온도로
LegacySensor를 생성한 다음, 이를LegacySensorAdapter로 감쌉니다. 어댑터에서getTemperature()를 호출하고 결과를 소수점 둘째 자리까지 반올림하여 출력합니다.출력 형식은 다음과 같아야 합니다:
Temperature: [celsius]C
이 챌린지는 어댑터 패턴을 통해 기존 코드를 수정하지 않고도 호환되지 않는 인터페이스를 통합하는 방법을 보여줍니다. 기상 애플리케이션은 TemperatureSensor 인터페이스하고만 작동하며, 실제 판독값이 레거시 화씨 센서에서 온다는 사실을 전혀 알지 못합니다.
치트 시트
어댑터 패턴(Adapter Pattern)은 호환되지 않는 인터페이스를 가진 객체들이 서로 협력할 수 있도록 그 사이에서 다리 역할을 하는 구조적 디자인 패턴입니다.
기본 구조
애플리케이션이 기대하는 인터페이스를 정의합니다:
<?php
interface PaymentProcessor {
public function pay(float $amount): string;
}다른 인터페이스를 가진 서드파티 클래스:
<?php
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}어댑터 생성하기
어댑터는 기대하는 인터페이스를 구현하는 동시에 내부적으로 서드파티 클래스를 사용합니다:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}사용법
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
// 출력: Charged 2999 USD via Stripe어댑터는 인터페이스 간의 변환을 담당하여, 서드파티 라이브러리를 통합하거나 구현체를 교체할 때 애플리케이션 코드를 변경하지 않고 유지할 수 있게 해줍니다.
직접 해보기
<?php
require_once 'LegacySensorAdapter.php';
// 입력 읽기
$fahrenheit = floatval(trim(fgets(STDIN)));
// TODO: 입력된 온도로 LegacySensor 생성
// TODO: LegacySensorAdapter로 감싸기
// TODO: 어댑터에서 getTemperature() 호출
// TODO: 결과를 소수점 둘째 자리까지 반올림하여 출력
// 출력 형식: Temperature: [celsius]C
?>이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8매직 메서드
매직 메서드 소개__toString 및 __debugInfo__get, __set, __isset, __unset__call 및 __callStatic__clone 및 객체 복제__serialize 및 __unserialize요약 - 커스텀 컬렉션