<markdown> <markdown>
전략 패턴(Strategy Pattern)
소개
- 알고리즘을 캡슐화하여 동적으로 교체할 수 있는 구조를 제공.
- 정책 패턴(Policy Pattern)이라고도 불림.
- 함수를 캡슐화해서 사용할 수도 있음을 인지하고 있자.
전략 패턴의 구성 요소
- 전략(Strategy) 인터페이스는 알고리즘을 정의하는 메서드를 선언합니다.
- 구체적인 전략(Concrete Strategies) 클래스는 전략 인터페이스를 구현하여 실제 알고리즘을 제공합니다.
- 컨텍스트(Context) 클래스는 전략을 사용하여 작업을 수행합니다.
전략 패턴의 예제
“여러 종류의 오리를 구현하고 싶다”
↪Fly기능을 추가하고 싶다.
↪그러나 가령 RubberDuck(고무오리) 등의 오리는 날 수 없기 때문에,
슈퍼 클래스에 메서드를 추가하는 것은 적합하지 않다.
↪물론 Override할 수 있겠지만, 오리의 개수와 종류가 점점 추가된다면 계속해서 Override해야 할 것이다.
해결책
↪따라서 우리는 하나의 슈퍼 클래스에서 유지되는 부분(공통된 부분)과 달라지는 부분(차이나는 부분)으로 분리해야한다.
- 이것을 캡슐화라고 한다.
이에 따라 우리는 위의 구성요소에서 말했 듯 인터페이스를 사용한다.
예제 코드
// 행동 인터페이스 public interface IFlyBehavior { void Fly(); } public interface IQuackBehavior { void Quack(); }
// 날 수 있는 행동 구현 public class FlyWithWings : IFlyBehavior { public void Fly() { Console.WriteLine("날개로 납니다."); } } // 날지 못하는 행동 구현 public class FlyNoWay : IFlyBehavior { public void Fly() { Console.WriteLine("날지 못합니다."); } } // 꽥꽥 소리를 내는 행동 구현 public class Quack : IQuackBehavior { public void Quack() { Console.WriteLine("꽥꽥 소리를 냅니다."); } } // 삑삑 소리를 내는 행동 구현 public class Squeak : IQuackBehavior { public void Quack() { Console.WriteLine("삑삑 소리를 냅니다."); } }
public abstract class Duck { protected IFlyBehavior flyBehavior; protected IQuackBehavior quackBehavior; public Duck() { } public abstract void Display(); public void PerformFly() { flyBehavior.Fly(); } public void PerformQuack() { quackBehavior.Quack(); } public void Swim() { Console.WriteLine("모든 오리는 물에 뜹니다."); } }
public class MallardDuck : Duck { public MallardDuck() { flyBehavior = new FlyWithWings(); quackBehavior = new Quack(); } public override void Display() { Console.WriteLine("Mallard 오리"); } } public class RubberDuck : Duck { public RubberDuck() { flyBehavior = new FlyNoWay(); quackBehavior = new Squeak(); } public override void Display() { Console.WriteLine("Rubber 오리"); } }
class Program { static void Main(string[] args) { Duck mallardDuck = new MallardDuck(); Duck rubberDuck = new RubberDuck(); mallardDuck.Display(); mallardDuck.PerformFly(); mallardDuck.PerformQuack(); rubberDuck.Display(); rubberDuck.PerformFly(); rubberDuck.PerformQuack(); } }
상점 시스템 구현
할인율
상점에서 주문 시스템을 구현한다고 가정해봅시다.
주문 처리 시스템은 다양한 할인 전략을 적용할 수 있어야 하며, 각 주문에 대한 할인을 유연하게 관리할 수 있어야 합니다.
예제 코드
// 할인 전략 인터페이스 public interface IDiscountStrategy { double ApplyDiscount(double amount); }
// 고정 금액 할인 전략 public class FixedDiscountStrategy : IDiscountStrategy { private double discountAmount; public FixedDiscountStrategy(double amount) { discountAmount = amount; } public double ApplyDiscount(double amount) { return amount - discountAmount; } } // 백분율 할인 전략 public class PercentageDiscountStrategy : IDiscountStrategy { private double percentage; public PercentageDiscountStrategy(double percent) { percentage = percent; } public double ApplyDiscount(double amount) { return amount * (1 - (percentage / 100)); } } // 할인 없음 전략 public class NoDiscountStrategy : IDiscountStrategy { public double ApplyDiscount(double amount) { return amount; } }
public class Order { private IDiscountStrategy discountStrategy; private double totalAmount; public Order(IDiscountStrategy strategy) { discountStrategy = strategy; } public void AddItem(double price) { totalAmount += price; } public double CalculateTotal() { return discountStrategy.ApplyDiscount(totalAmount); } }
class Program { static void Main(string[] args) { Order order1 = new Order(new FixedDiscountStrategy(10)); // 10 달러 할인 order1.AddItem(50); // 50 달러 상품 추가 order1.AddItem(30); // 30 달러 상품 추가 double total1 = order1.CalculateTotal(); Console.WriteLine("첫 번째 주문의 총액: " + total1 + " 달러"); Order order2 = new Order(new PercentageDiscountStrategy(20)); // 20% 할인 order2.AddItem(100); // 100 달러 상품 추가 order2.AddItem(75); // 75 달러 상품 추가 double total2 = order2.CalculateTotal(); Console.WriteLine("두 번째 주문의 총액: " + total2 + " 달러"); Order order3 = new Order(new NoDiscountStrategy()); // 할인 없음 order3.AddItem(40); // 40 달러 상품 추가 double total3 = order3.CalculateTotal(); Console.WriteLine("세 번째 주문의 총액: " + total3 + " 달러"); } }
주문처리
예를 들어, 일부 주문은 신용 카드로 처리되고, 일부 주문은 현금으로 처리될 수 있습니다. 이러한 주문 처리 방식을 각각의 전략 클래스로 구현하고, 컨텍스트 클래스에서 해당 전략을 선택하여 주문을 처리할 수 있습니다.
전략 패턴의 장점
- 유연성
- 확장성
- 재사용성
- 테스트의 용이함
결론
우리는 상속으로 코드의 재사용성과 유연성을 높일 수 있다.
그러나 코드의 전체적인 이해도가 부족하다면 오히려 예외 처리에 있어서 서브 클래스에 악영향을 끼쳐서 불필요한 코드 중복이 발생할 수 있다.
이런 경우, 전략 패턴으로 알고리즘의 동적 교체와 재사용성을 제공하여 코드의 유연성을 높여 보완할 수 있다.
Reference
Uploaded by N2T