전략 패턴
Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 74번째.
전략 패턴(Strategy Pattern)은 알고리즘군을 정의하고 각각을 별도의 클래스에 캡슐화하여 실행 중에 이들을 서로 교체할 수 있게 하는 행동 디자인 패턴입니다. 전략을 사용하는 객체는 자신이 어떤 알고리즘을 실행하고 있는지 알 필요가 없습니다.
다양한 할인 전략을 적용할 수 있는 쇼핑카트를 상상해 보세요. 조건문을 사용하여 할인 로직을 하드코딩하는 대신, 각 할인을 별도의 클래스로 정의합니다:
<?php
interface DiscountStrategy {
public function calculate(float $total): float;
}
class NoDiscount implements DiscountStrategy {
public function calculate(float $total): float {
return $total;
}
}
class PercentageDiscount implements DiscountStrategy {
public function __construct(private int $percent) {}
public function calculate(float $total): float {
return $total - ($total * $this->percent / 100);
}
}
class FixedDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return max(0, $total - $this->amount);
}
}
컨텍스트 클래스는 어떤 전략이든 수용하며 계산을 위임합니다:
<?php
class ShoppingCart {
public function __construct(private DiscountStrategy $discount) {}
public function setDiscount(DiscountStrategy $discount): void {
$this->discount = $discount;
}
public function checkout(float $total): float {
return $this->discount->calculate($total);
}
}
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";
$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);
출력:
100
80전략 패턴(Strategy Pattern)은 연산을 수행하는 여러 방법이 있고, 이를 사용하는 클래스를 수정하지 않고도 그 방법들 사이를 전환하고 싶을 때 효과적입니다. 새로운 할인 유형을 추가하는 것은 기존 코드를 수정하는 것이 아니라 새로운 클래스를 생성하는 것을 의미합니다.
챌린지
쉬움전략 패턴(Strategy Pattern)을 사용하여 배송비 계산기를 만들어 보겠습니다. 배송 방법마다 가격 책정 규칙이 다릅니다. 표준 배송은 고정 요금을 부과하고, 특급 배송은 무게에 따른 비율을 추가하며, 익일 배송은 두 가지를 결합합니다. 전략 패턴을 사용하면 이를 사용하는 코드를 변경하지 않고도 이러한 계산 방식을 교체할 수 있습니다.
코드는 네 개의 파일로 구성됩니다:
ShippingStrategy.php— 모든 배송 방법의 계약을 정의하는ShippingStrategy인터페이스를 생성합니다. 이 인터페이스는 패키지 무게를 받아 배송비를 반환하는calculate(float $weight): float메서드를 가져야 합니다.ShippingMethods.php— ShippingStrategy 파일을 포함하고 인터페이스를 구현하는 세 개의 클래스를 생성합니다:StandardShipping— 무게에 상관없이 고정 요금5.00달러 부과ExpressShipping— 킬로그램당2.00달러 부과 (무게에 2를 곱함)OvernightShipping— 기본 요금10.00달러에 킬로그램당3.00달러 추가 부과
ShippingCalculator.php— ShippingStrategy 파일을 포함하고 컨텍스트 역할을 하는ShippingCalculator클래스를 생성합니다. 계산기는 다음을 수행해야 합니다:- 생성자에서
ShippingStrategy를 받습니다 (생성자 프로모션 사용) - 런타임에 배송 방법을 변경할 수 있는
setStrategy(ShippingStrategy $strategy): void메서드를 가집니다 - 현재 전략에 계산을 위임하는
calculateCost(float $weight): float메서드를 가집니다
- 생성자에서
main.php— ShippingMethods 및 ShippingCalculator 파일을 포함합니다. 배송 유형("standard","express", 또는"overnight")과 패키지 무게라는 두 가지 입력을 받습니다.StandardShipping으로 시작하는ShippingCalculator를 생성합니다. 입력된 배송 유형에 따라setStrategy()를 사용하여 적절한 배송 방법으로 전환합니다. 그런 다음 주어진 무게에 대한 비용을 계산하고 소수점 둘째 자리까지 형식을 맞춰 결과를 출력합니다.출력 형식은 다음과 같아야 합니다:
Shipping cost: $[cost]
이 챌린지는 전략 패턴이 배송 시스템을 얼마나 유연하게 만드는지 보여줍니다. SameDayShipping과 같은 새로운 배송 방법을 추가할 때 계산기나 기존 배송 방법을 수정하지 않고 인터페이스를 구현하는 새 클래스만 생성하면 됩니다.
치트 시트
전략 패턴(Strategy Pattern)은 알고리즘 군을 정의하고, 각각을 별도의 클래스에 캡슐화하며, 실행 시점에 이들을 서로 교체하여 사용할 수 있게 하는 행동 디자인 패턴입니다.
전략을 위한 인터페이스를 정의합니다:
<?php
interface DiscountStrategy {
public function calculate(float $total): float;
}구체적인 전략들을 구현합니다:
<?php
class NoDiscount implements DiscountStrategy {
public function calculate(float $total): float {
return $total;
}
}
class PercentageDiscount implements DiscountStrategy {
public function __construct(private int $percent) {}
public function calculate(float $total): float {
return $total - ($total * $this->percent / 100);
}
}
class FixedDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return max(0, $total - $this->amount);
}
}전략을 사용하는 컨텍스트 클래스를 생성합니다:
<?php
class ShoppingCart {
public function __construct(private DiscountStrategy $discount) {}
public function setDiscount(DiscountStrategy $discount): void {
$this->discount = $discount;
}
public function checkout(float $total): float {
return $this->discount->calculate($total);
}
}전략을 주입하여 패턴을 사용합니다:
<?php
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100); // 100
$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100); // 80전략 패턴을 사용하면 이를 사용하는 클래스를 수정하지 않고도 알고리즘을 전환할 수 있습니다. 새로운 전략을 추가하려면 인터페이스를 구현하는 새로운 클래스를 생성하기만 하면 됩니다.
직접 해보기
<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';
// 입력 읽기
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));
// TODO: StandardShipping으로 시작하는 ShippingCalculator 생성
// TODO: 입력된 배송 유형("standard", "express" 또는 "overnight")에 따라,
// setStrategy()를 사용하여 적절한 배송 방법으로 전환
// TODO: 주어진 무게에 대한 비용 계산
// TODO: 소수점 둘째 자리까지 형식을 맞춰 결과 출력
// 출력 형식: Shipping cost: $[cost]
?>이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8매직 메서드
매직 메서드 소개__toString 및 __debugInfo__get, __set, __isset, __unset__call 및 __callStatic__clone 및 객체 복제__serialize 및 __unserialize요약 - 커스텀 컬렉션