[Design Pattern] 전략 패턴(Strategy Pattern)

뚜비·2023년 3월 14일
0

Design Pattern

목록 보기
4/9
post-thumbnail

💻 전략 패턴 구현 코드


전략 패턴

"동일 계열의 알고리즘군을 정의하고, 각 알고리즘을 캡슐화하며, 이들을 상호교환이 가능하도록 만듭니다. 알고리즘을 사용하는 클라이언트와 상관없이 독립적으로 알고리즘을 다양하게 변경할 수 있게 합니다"
-Gang Of Four-

  • 정책 패턴(policy pattern)이라고도 하며, 객체의 행위를 바꾸고 싶은 경우 전략이라고 부르는 ‘캡슐화한 알고리즘’을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴
  • 객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화 하는 인터페이스를 정의하여, 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장
    → 공통된 인터페이스 A를 갖는 객체들(E, F, G)을 B 클래스에서 사용할때, 인터페이스 A만을 이용하여 들어오는 객체 종류(E, F, G)에 따라 동작
  • GoF의 디자인 패턴 중 행위 패턴에 해당

🤔언제 사용하나요?
1. 같은 기능이지만 서로 다른 전략을 가진 클래스들을 상호교환하고 싶을 때
2. 실행 중에 동적으로 클래스의 동작 혹은 알고리즘을 선택하고 싶을 때


구조

  • Context
    : Strategy를 이용하는 역할, Strategy1의 인스턴스를 가지고 있으며 이용
    : Strategy의 인터페이스(API)를 호출
  • Strategy
    : 전략을 이용하기 위한 인터페이스(API)를 결정
  • Strategy1
    : Startegy의 인터페이스(API)를 실제로 구현, 구체적인 전략(작전, 방책, 방법, 알고리즘)을 실제로 프로그래밍한다.


장점

  • 유지보수 하기 좋다 : 알고리즘을 수정하고 싶을 때 Strategy 인터페이스는 변경하지 않고 Strategy1의 구현만 수정하면 된다.
  • 느슨한 연결 : 위임이라는 느슨한 연결을 사용하고 있으므로 알고리즘을 용이하게 교환할 수 있다. 예를 들어 장기 게임을 하는 프로그램에서 사용자의 선택에 맞추어 사고 루틴의 레벨을 간단하게 교체할 수 있따.
  • 프로그램의 동작 중에 클래스를 교체할 수 있음 : 예를 들어 메모리가 적은 환경에서 SlowbutLessMemoryStrategy(속도는 느리지만 메모리를 절약)를 사용하고 메모리가 많은 환경에서 FastButMoreStrategy(속도는 빠르지만 메모리를 많이 사용)를 사용.


구현

  • Strategy(전략 인터페이스)
    ⌞ OperationAdd (구체적인 전략)
    ⌞ OperationSubstract (구체적인 전략)
    ⌞ OperationMultiply (구체적인 전략)
  • Context (전략을 사용하는 클래스)
  • StrategyPatternDemo (걍 Main 클래스)

  1. Strategy 인터페이스를 생성
public interface Strategy {
   public int doOperation(int num1, int num2);
}

  1. 구체적인 Strategy 구현
  • doOperation()은 Strategy의 API
/* 구체적인 전략 구현 */
public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

public class OperationSubstract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

  1. Context 클래스 구현
  • Strategy를 사용
public class Context {
   private Strategy strategy;

   public Context(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){ // 기능이 동일하지만 각기 다른 전략을 사용
      return strategy.doOperation(num1, num2);
   }
}

  1. Demo
public class StrategyPatternDemo {
   public static void main(String[] args) {
      Context context = new Context(new OperationAdd());		
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); // 전략 1 사용 

      context = new Context(new OperationSubstract());		
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); // 전략 2 사용

      context = new Context(new OperationMultiply());		
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5)); // 전략 3 사용
   }
}

// 10 + 5 = 15
// 10 - 5 = 5
// 10 * 5 = 50


EXAMPLE

Java

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/* Strategy 인터페이스 */
interface PaymentStrategy { 
    public void pay(int amount);
} 

/* 전략1 클래스 */
class KAKAOCardStrategy implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;
    
    public KAKAOCardStrategy(String nm, String ccNum, String cvv, String expiryDate){
        this.name=nm;
        this.cardNumber=ccNum;
        this.cvv=cvv;
        this.dateOfExpiry=expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount +" paid using KAKAOCard.");
    }
} 

/* 전략2 클래스 */
class LUNACardStrategy implements PaymentStrategy {
    private String emailId;
    private String password;
    
    public LUNACardStrategy(String email, String pwd){
        this.emailId=email;
        this.password=pwd;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using LUNACard.");
    }
} 

/* item 클래스 */
class Item { 
    private String name;
    private int price; 
    public Item(String name, int cost){
        this.name=name;
        this.price=cost;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }
} 

/* Context 클래스 */
class ShoppingCart { 
    List<Item> items;
    
    public ShoppingCart(){
        this.items=new ArrayList<Item>();
    }
    
    public void addItem(Item item){
        this.items.add(item);
    }
    
    public void removeItem(Item item){
        this.items.remove(item);
    }
    
    public int calculateTotal(){
        int sum = 0;
        for(Item item : items){
            sum += item.getPrice();
        }
        return sum;
    }
    
    public void pay(PaymentStrategy paymentMethod){
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}  

/* Main */
public class HelloWorld{
    public static void main(String []args){
        ShoppingCart cart = new ShoppingCart();
        
        Item A = new Item("kundolA",100);
        Item B = new Item("kundolB",300);
        
        cart.addItem(A);
        cart.addItem(B);
        
        // pay by LUNACard
        cart.pay(new LUNACardStrategy("kundol@example.com", "pukubababo"));
        // pay by KAKAOBank
        cart.pay(new KAKAOCardStrategy("Ju hongchul", "123456789", "123", "12/01"));
    }
}
/*
400 paid using LUNACard.
400 paid using KAKAOCard.
*/
  • 쇼핑 카트아이템을 담아 LUNACard 또는 KAKAOCard라는 두 개의 전략으로 결제하는 코드
  • 어떤 것을 살 때 네이버페이, 카카오페이 등 다양한 방법으로 결제하듯, 어떤 아이템을 살 때 LUNACard로 사는 것과 KAKAOCard로 사는 것을 구현한 예제
  • 결제 방식의 ‘전략’만 바꿔서 두 가지 방식으로 결제하는 것을 구현

Passport의 전략 패턴

🤔 passport란?

  • Node.js에서 인증 모듈을 구현할 때 쓰는 미들웨어 라이브러리
  • 여러 가지 ‘전략’을 기반으로 인증할 수 있게함
  • 서비스 내의 회원가입된 아이디와 비밀번호를 기반으로 인증하는 LocalStrategy 전략과 페이스북, 네이버 등 다른 서비스를 기반으로 인증하는 OAuth 전략 등을 지원
// passport와 passport-local 모듈을 가져오기 
var passport = require('passport')
    , LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
    function(username, password, done) // 아이디와 비밀번호를 파라미터로 받음 
  	{
        User.findOne({ username: username }, function (err, user) {
          if (err) { return done(err); }
            if (!user) {
                return done(null, false, { message: 'Incorrect username.' });
            }
            if (!user.validPassword(password)) {
                return done(null, false, { message: 'Incorrect password.' });
            }
            return done(null, user);
        });
    }
));
  • passport.use(new LocalStrategy( ...처럼 passport.use()라는 메서드에 ‘전략’을 매개변수로 넣어서 로직을 수행


😎 참고자료

면접을 위한 CS 전공지식 노트
Java 언어로 배우는 디자인 패턴 입문
GOF Design Pattern
Strategy pattern
Design Patterns - Strategy Pattern
디자인패턴 이해하기
[디자인패턴] 전략 패턴 ( Strategy Pattern )
전략패턴
전략패턴 - 기계인간

profile
SW Engineer 꿈나무 / 자의식이 있는 컴퓨터

0개의 댓글