[Design Pattern] 1. 전략 패턴 Strategy Pattern

neo-the-cow·2023년 10월 20일
0

디자인 패턴

목록 보기
1/1
post-thumbnail

0. 들어가기 전에

0.1 전략패턴이란?

  • Strategy(or policy) 패턴으로 객체의 행동 전략을 상황에 맞게 캡슐화 하고 필요한 행위를 선택적으로 실행할 수 있게 해줍니다.
  • 큰 갈래는 같지만 세부적인 동작이 다른 경우 세부 동작을 미리 정의해 알맞는 알고리즘을 교체 할 수 있도록 도와주는 디자인 패턴입니다.

0.2 대략적인 구조

  • 전략패턴의 대략적인 구조는 위와같이 나타낼 수 있습니다.
  • 클라이언트가 컨텍스트의 메서드를 호출하면 컨텍스트는 응답을 위해 다음과 같이 미리 등록 된 전략을 통해 결과를 반환 합니다.

0.3 장점

전략패턴은 대표적으로 다음과 같은 장점을 가집니다.

  • 유지보수성
    • 새로운 전략의 추가나 기존 전략의 수정 시 다른 부분에 미치는 영향을 최소화 할 수 있습니다.
  • 테스트 용이성
    • 각각의 전략 구현체들을 독립적으로 테스트 할 수 있습니다.
  • 동적 알고리즘 선택
    • 런타임 중에 동적으로 전략을 교체하며 시스템의 동작을 조절 할 수 있습니다.

1. 자바 코드로 알아보기

1.1 자판기 머신

  • 간단한 자판기 머신의 동작을 예시로 구성해보겠습니다.
  • 자판기는 자신이 보유한 음료의 목록을 가지고 있고 각각의 선택 버튼에 따라 출력하는 음료가 달라집니다.

1.1.1 패키지 구조

1.1.2 코드

import context.VendingMachine;
import strategy.impl.ColaVendingStrategy;
import strategy.impl.WaterVendingStrategy;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world!");

        VendingMachine machine = new VendingMachine();

        machine.setVendingStrategy(new WaterVendingStrategy());
        machine.vend();

        machine.setVendingStrategy(new ColaVendingStrategy());
        machine.vend();
    }
}
package context;

import strategy.VendingStrategy;

public class VendingMachine {

    private VendingStrategy vendingStrategy;

    public void setVendingStrategy(VendingStrategy strategy) {
        this.vendingStrategy = strategy;
    }

    public void vend() {
        vendingStrategy.vend();
    }

}
package strategy;

public interface VendingStrategy {

    void vend();

}
package strategy.impl;

import strategy.VendingStrategy;

public class WaterVendingStrategy implements VendingStrategy {

    @Override
    public void vend() {
        System.out.println("생수");
    }

}
package strategy.impl;

import strategy.VendingStrategy;

public class ColaVendingStrategy implements VendingStrategy {

    @Override
    public void vend() {
        System.out.println("콜라");
    }

}

2. 스프링 코드로 알아보기

@RestController
public class AuthController {

    private final Map<String, AuthService> authServices;
    private final String serviceSuffix;
    
    ...

    @Autowired
    public AuthController(
            Map<String, AuthService> authServices,
            ...
    ) {
        this.authServices = authServices;
        this.serviceSuffix = "AuthService";
        ...
    }

    @GetMapping("/auth/{provider}/client-id")
    public ResponseEntity<?> getClientId(
            @PathVariable String provider
    ) {
    	AuthService service = getServiceForProvider(provider);
		...
    }

    @PostMapping("/auth/{provider}")
    public ResponseEntity<?> authenticateViaGitHub(
            @RequestBody XXXOAuth2Dto dto,
            @PathVariable String provider
    ) {
		AuthService service = getServiceForProvider(provider);
        ...
    }

    private AuthService getServiceForProvider(String provider) {
        return authServices.computeIfAbsent(
        		provider + AuthService.class.getSimpleName(),
                key -> {
                	throw new RuntimeException();
                }
        );
    }

}
  • 실제로 스프링 코드에선 OAuth2를 이용한 인증과정을 예시로 들 수 있습니다.
  • 컨트롤러 레이어에서 provider 경로 변수를 받아 serviceSuffix와 결합해 미리 주입받은 빈 이름으로 서비스레이어 구현체를 찾는 방식입니다.
  • 자바코드에서는 구현체 객체를 만들어 클라이언트가 직접 주입해주었다면 스프링에선 구현체 목록을 스프링 빈으로 등록해 Map<String, {Interface}> 형태로 미리 주입받을 수 있습니다.
  • AuthController가 컨텍스트가 되고 AuthService가 전략 인터페이스, 그 구현체들이 구체적인 전략의 구현체가 될 수 있습니다.

3. 맺음말

간단하게 전략패턴에 대해 자바코드와 스프링 코드 예시로 소개했습니다.
혹시나 오류가 발생한다거나 결과가 올바르지 않다면 중간에 오탈자는 없었는지, 빠진건 없는지 한번 다시 확인해 주시고 이해가 안되는 부분이 있다면 댓글로 남겨주시면 확인하는 대로 답변 달겠습니다.

profile
Hi. I'm Neo

0개의 댓글