[개발자_객체지향_디자인패턴] 디자인패턴 - 전략 패턴

박상준·5일 전
0

소개

  • 모든 동작을 하나의 클래스에서 직접 포함하면 유지 보수가 어려움.
  • 전략 패턴은 알고리즘을 별도의 클래스로 캡슐화하여 유연성을 제공.
    • OCP 원칙에 따라 확장에 열려있고 수정엔 닫혀있는 구조를 만들 수 있음.

문제 상황

  • 한 과일 매장에서 특정 조건에 따라 다른 가격 할인 정책을 적용하고 있음.
    1. 첫 손님 할인
      1. 하루의 첫 손님에게 10% 할인을 적용함
    2. 덜 신선한 과일 할인
      1. 저녁 시간대에 신선도가 떨어진 과일에 20%의 할인을 적용한다
  • 초기 코드
    public class Calculator {
    
        public int calculate(boolean firstGuest, List<Item> items) {
            int sum = 0;
            for (Item item : items) {
                if (firstGuest) {
                    sum += (int) (item.getPrice() * 0.9); // 첫 손님 10% 할인
                } else if (!item.isFresh()) {
                    sum += (int) (item.getPrice() * 0.8); // 덜 신선한 과일 20% 할인
                } else {
                    sum += item.getPrice();
                }
            }
            return sum;
        }
    }
    
  • 문제점
    1. 여러 할인 정책이 한 메서드가 섞여 있어 코드의 가독성과 유지 보수가 어렵낟.
    2. 새로운 할인 정책을 추가시마다 calculate 메서드를 수정해야함.
      1. OCP 정책을 위반함.

전략 패턴 사용

  • 할인 정책의 분리
    • 각 할인 정책을 별도의 클래스로 캡슐화하기
  • 공통 인터페이스를 정의하기
    • 모든 할인 전략이 구현하는 인터페이스를 생성하기
  • 콘텍스트 클래스 업데이트하기
    • Calculator 클래스가 할인 전략을 사용하도록 수정한다.

구조 개요

  • 콘텍스트(Calculator)
    • DiscountStrategy 를 사용하여 계산을 수행하는 클래스
  • 전략 인터페이스
    • 할인된 가격을 계산하는 메서드를 정의하는 인터페이스이다.
  • 구체적인 전략
    • 특정 할인 정책을 구현하는 클래스들.

구현 세부사항

  1. DiscountStrategy 인터페이스

    public interface DiscountStrategy {
        int getDiscountPrice(Item item);
    }
    
  2. 구체적인 전략 클래스

    • 첫 손님 할인 전략
      public class FirstGuestDiscountStrategy implements DiscountStrategy {
      
          @Override
          public int getDiscountPrice(Item item) {
              return (int) (item.getPrice() * 0.9); // 10% 할인 적용
          }
      }
      
    • 덜 신선한 과일 할인 전략
      public class NonFreshItemDiscountStrategy implements DiscountStrategy {
      
          @Override
          public int getDiscountPrice(Item item) {
              return (int) (item.getPrice() * 0.8); // 20% 할인 적용
          }
      }
      
  3. 계산 클래스

    public class Calculator {
    
        private DiscountStrategy discountStrategy;
    
        public Calculator(DiscountStrategy discountStrategy) {
            this.discountStrategy = discountStrategy;
        }
    
        public int calculate(List<Item> items) {
            int sum = 0;
            for (Item item : items) {
                sum += discountStrategy.getDiscountPrice(item);
            }
            return sum;
        }
    }
    
    • DiscountStrategy 는 런타임중에 주입된다
    • 계산시 사람의 특성에 따라 전략이 선택된다.

클라이언트 상호작용

  • 첫 손님 할인
    public class Checkout {
    
        public void processFirstGuestPurchase(List<Item> items) {
            DiscountStrategy strategy = new FirstGuestDiscountStrategy();
            Calculator calculator = new Calculator(strategy);
            int total = calculator.calculate(items);
            // 계산된 총액으로 결제 진행
        }
    }
    
  • 덜 신선한 과일 할인 적용
    public class Checkout {
    
        public void processNonFreshItemsPurchase(List<Item> items) {
            DiscountStrategy strategy = new NonFreshItemDiscountStrategy();
            Calculator calculator = new Calculator(strategy);
            int total = calculator.calculate(items);
            // 계산된 총액으로 결제 진행
        }
    }
    

전략패턴의 확장

  • 새로운 할인 정책이 추가되는 경우
    • 마지막 손님 할인으로 50% 할인을 제공하고자 하는 경우
  1. 새로운 전략 구현

    public class LastGuestDiscountStrategy implements DiscountStrategy {
    
        @Override
        public int getDiscountPrice(Item item) {
            return (int) (item.getPrice() * 0.5); // 50% 할인 적용
        }
    }
    
  2. 클라이언트 코드 업데이트

    public class Checkout {
    
        public void processLastGuestPurchase(List<Item> items) {
            DiscountStrategy strategy = new LastGuestDiscountStrategy();
            Calculator calculator = new Calculator(strategy);
            int total = calculator.calculate(items);
            // 계산된 총액으로 결제 진행
        }
    }
    

장점.

  1. 기존 클래스 변경이 필요가 없다
    1. Calculator 클래스의 수정이 필요없다.
  2. 개방 - 폐쇄 원칙의 준수
    1. 새로운 전략을 추가하더라도 기존 코드가 그대로 유지된다.

전략 패턴은 언제 써야하나

조건부 로직 대체의 경우

  • 복잡한 조건문을 전략 구현으로 대체하는 경우

알고리즘 변형이 필요한 경우

  • 동일한 작성을 다른 방식으로 수행하는 여러 알고리즘이 있는 경우

실행 시간 결정

  • 실행 시간에 따라 알고리즘을 변경해야 하는 경우
profile
이전 블로그 : https://oth3410.tistory.com/

0개의 댓글