# [Spring] λ””μžμΈ νŒ¨ν„΄ - Strategy Pattern
Study Repository

[Spring] λ””μžμΈ νŒ¨ν„΄ - Strategy Pattern

by rlaehddnd0422

이 μ „ ν¬μŠ€νŒ…μ—μ„œ μ•Œμ•„λ³Έ Template Method Pattern은 "좔상 ν΄λž˜μŠ€"에 μžμ‹ ν΄λž˜μŠ€μ—μ„œ λ³€κ²½λ˜λŠ” λ‘œμ§μ˜ κ³¨κ²©μ„ μ„ μ–Έ λ° κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•˜λŠ” λ‘œμ§μ˜ κ³¨κ²©μ„ μ •μ˜(*κ΅¬ν˜„)ν•˜κ³  "μžμ‹ ν΄λž˜μŠ€"μ—μ„œλŠ” λΆ€λͺ¨ ν΄λž˜μŠ€λ₯Ό μƒμ†λ°›μ•„ λ³€κ²½λ˜λŠ” λ‘œμ§μ„ μ •μ˜(*κ΅¬ν˜„)ν•˜λŠ” λ°©μ‹μ΄μ˜€μŠ΅λ‹ˆλ‹€.

이 νŒ¨ν„΄μ—μ„œλŠ” μƒμ†μ„ μ‚¬μš©ν•˜μ—¬ "νŠΉμ •" λΆ€λͺ¨ ν΄λž˜μŠ€μ— μ˜μ‘΄ν•˜μ§€λ§Œ, μ•„λž˜ μ½”λ“œμ™€ κ°™μ΄ μžμ‹ ν΄λž˜μŠ€ μž…μž₯μ—μ„œ 'κ΅¬ν˜„ ν΄λž˜μŠ€'만 λ”± μƒκ°ν•΄λ³΄λ©΄ λΆ€λͺ¨ ν΄λž˜μŠ€μ˜ κΈ°λŠ₯을 μ „ν˜€ μ‚¬μš©ν•˜μ§€λ„ μ•ŠλŠ”데, λΆ€λͺ¨ ν΄λž˜μŠ€μ˜ μ˜ν–₯을 λ°›κ²Œ λ©λ‹ˆλ‹€.

@Slf4j
public class SubClassLogic1 extends AbstractTemplate {
    @Override
    protected void call() {
        log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직1 μ‹€ν–‰");
    }
}


+ λ³„λ„μ˜ μžμ‹ ν΄λž˜μŠ€λ‚˜ μ΅λͺ… ν΄λž˜μŠ€λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•˜λŠ” λΆ€λΆ„도 κ½€ λ²ˆκ±°λ‘­λ‹€λŠ” λ‹¨μ μ΄ μžˆμŠ΅λ‹ˆλ‹€.

 

μ΄λ²ˆμ—λŠ” Template-Method νŒ¨ν„΄μ˜ λ‹¨μ μ„ λ³΄μ™„ν•  μˆ˜ μžˆλŠ” Strategy νŒ¨ν„΄μ— λŒ€ν•΄ μ•Œμ•„보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.!

Strategy νŒ¨ν„΄μ΄λž€?

 

 

strategy νŒ¨ν„΄

μ‹¬ν”Œν•˜κ²Œ μ„€λͺ…ν•˜μžλ©΄ μ΄λ ‡μŠ΅λ‹ˆλ‹€.

  • λ³€ν•˜μ§€ μ•ŠλŠ” 뢀뢄을 Context에 클래슀둜 μ •μ˜ 및 κ΅¬ν˜„
    • Contextκ°€ Template Method Pattern의 ν…œν”Œλ¦Ώ 역할을 ν•œλ‹€κ³  λ³΄μ‹œλ©΄ λ©λ‹ˆλ‹€.
  • λ³€ν•˜λŠ” 뢀뢄을 Strategy에 μΈν„°νŽ˜μ΄μŠ€λ‘œ μ •μ˜ 및 μ„ μ–Έ
    • Strategy μΈν„°νŽ˜μ΄μŠ€μ˜ κ΅¬ν˜„μ²΄λ‘œ λ³€ν•˜λŠ” 뢀뢄을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.
    • StrategyλŠ” Template Method Pattern의 λ³€ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜ μ—­ν• 
  • Spring을 λ‹€λ£¨λ©΄μ„œ 기본적으둜 μ‚¬μš©ν•΄μ˜¨ 방식이 λ°”λ‘œ μ „λž΅ νŒ¨ν„΄μ΄λΌκ³  λ³Ό 수 있음.
μ „λž΅ νŒ¨ν„΄(Strategy Pattern)의 μ˜λ„?
μ•Œκ³ λ¦¬μ¦˜ μ œν’ˆκ΅°μ„ μ •μ˜ν•˜κ³  각각을 μΊ‘μŠν™”ν•˜μ—¬ μƒν˜Έ κ΅ν™˜ κ°€λŠ₯ν•˜κ²Œ λ§Œλ“€μ–΄ μ•Œκ³ λ¦¬μ¦˜μ„ λ…λ¦½μ μœΌλ‘œ λ³€κ²½ κ°€λŠ₯ν•˜κ²Œ λ§Œλ“€μž. 

 

Strategy νŒ¨ν„΄ v1 - Context 생성 μ‹œμ μ— μ˜μ‘΄κ΄€κ³„ μ£Όμž… 

Strategy

public interface Strategy {
    void call();
}
  • call() : λ³€ν•˜λŠ” 뢀뢄인 골격을 μΈν„°νŽ˜μ΄μŠ€λ‘œ μ„ μ–Έν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. 
  • κ΅¬ν˜„μ²΄μ— 따라 μœ λ™μ μœΌλ‘œ κ΅¬ν˜„ 
@Slf4j
public class StrategyLogic1 implements Strategy {

    @Override
    public void call() {
        log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직1 μ‹€ν–‰");
   }
}
@Slf4j
public class StrategyLogic2 implements Strategy {

    @Override
    public void call() {
        log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직2 μ‹€ν–‰");
   }
}

Context

Contextμ—μ„œλŠ” Strategyλ₯Ό 컴파일 μ‹œμ μ—μ„œ κ°ˆμ•„λΌμšΈ 수 μžˆλ„λ‘ μ£Όμž…λ°›μ•„ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • λ³€ν•˜μ§€ μ•ŠλŠ” 뢀뢄인 곡톡 λ‘œμ§μ€ execute()에 κ΅¬ν˜„ν•˜κ³  λ³€ν•˜λŠ” 뢀뢄은 μ£Όμž…λ°›μ€ Strategy을 톡해 μœ„μž„ν•˜μ—¬ μ‹€ν–‰ν•©λ‹ˆλ‹€.
  • Strategy Pattern의 핡심 : ContextλŠ” Strategy μΈν„°νŽ˜μ΄μŠ€μ—λ§Œ μ˜μ‘΄ν•œλ‹€λŠ” 점
    • 즉, Strategy의 κ΅¬ν˜„μ²΄λ₯Ό λ³€κ²½ν•˜κ±°λ‚˜ μƒˆλ‘œ λ§Œλ“€μ–΄λ„ Context μ½”λ“œμ—λŠ” 영ν–₯을 주지 μ•ŠμŠ΅λ‹ˆλ‹€.
@Slf4j
public class ContextV1 {

    private Strategy strategy;

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

    public void execute() {
        long startTime = System.currentTimeMillis();
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰
        strategy.call(); // μœ„μž„
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ’…λ£Œ
        long endTime = System.currentTimeMillis();
        log.info("result Time = {}", endTime - startTime);
    }
}
  • 1. context - Strategy κ΅¬ν˜„μ²΄λ₯Ό μ£Όμž…λ°›μ•„ 생성
  • 2. execute() μ‹€ν–‰
    • 2-1. 곡톡 둜직 μ‹€ν–‰ 
    • 2-2. λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 : μ£Όμž…ν•œ Strategy κ΅¬ν˜„μ²΄μ— μœ„μž„ν•˜μ—¬ μ‹€ν–‰
  • 3. contextλŠ” λ‚˜λ¨Έμ§€ λ‘œμ§μ„ μ‹€ν–‰.
@Test
public void strategyTest() throws Exception {
    Strategy strategyLogic1 = new StrategyLogic1();
    ContextV1 context1 = new ContextV1(strategyLogic1);
    context1.execute();
}

+ Tip : λ§ˆμ°¬κ°€μ§€λ‘œ 읡λͺ… 클래슀λ₯Ό 톡해 Strategy κ΅¬ν˜„μ²΄ 생성해도 ok

 

+ Tip μΈν„°νŽ˜μ΄μŠ€μ— default, static을 μ œμ™Έν•˜κ³  λ©”μ†Œλ“œκ°€ 1개 μ„ μ–Έλ˜μ–΄ μžˆμ„ λ•Œ λžŒλ‹€μ‹ μ‚¬μš© κ°€λŠ₯ 

  • λ­”κ°€ μ΅μˆ™ν•œ 방법이라고 생각이 λ“€μ—ˆλŠ”λ°, Spring μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ°œλ°œν•  λ•Œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‘œλ”© μ‹œμ μ— μ˜μ‘΄κ΄€κ³„ μ£Όμž…μ„ 톡해 ν•„μš”ν•œ μ˜μ‘΄κ΄€κ³„λ₯Ό λͺ¨λ‘ 맺어두고 λ‚œ λ‹€μŒμ— μ‹€μ œ μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” 것과 같은 μ›λ¦¬μ˜€μŠ΅λ‹ˆλ‹€.
  • Context에 Strategyλ₯Ό μ£Όμž…λ°›μ•„ μ‚¬μš©ν•˜κ²Œ 되면 쑰립(μ£Όμž…) 이후 변경이 번거둭고 싱글톀 νŒ¨ν„΄μΌ λ•Œ λ™μ‹œμ„±κΉŒμ§€ κ³ λ €ν•΄μ£Όμ–΄μ•Ό ν•˜λŠ” λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. ( 이 경우 Contextλ₯Ό μƒˆλ‘œ μƒμ„±ν•΄μ„œ λ‹€λ₯Έ κ΅¬ν˜„μ²΄λ₯Ό μ£Όμž…λ°›μ•„ μ‚¬μš©ν•˜λŠ” 방법이 ꢌμž₯됨 ) 
  • 이어 μ„  쑰립 - ν›„ μ‚¬μš© 방식 말고 더 μœ μ—°ν•˜κ²Œ μ „λž΅ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λŠ” 방법을 μ•Œμ•„λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

Strategy νŒ¨ν„΄ v2 - Context μƒμ„± μ‹œμ μ΄ μ•„λ‹Œ λ©”μ†Œλ“œμ— Strategyλ₯Ό νŒŒλΌλ―Έν„° λ°”인딩 

λ°”λ‘œ μ•„λž˜μ™€ 같이 Context에 Strategyλ₯Ό μ£Όμž…ν•˜μ§€ μ•Šκ³  λ©”μ†Œλ“œμ— νŒŒλΌλ―Έν„°λ‘œ Strategyλ₯Ό 전달받아 μ‚¬μš©ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

@Slf4j
public class ContextV2 {

    public void execute(Strategy strategy) {
        long startTime = System.currentTimeMillis();
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰
        strategy.call(); // μœ„μž„
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ’…λ£Œ
        long endTime = System.currentTimeMillis();
        log.info("result Time = {}", endTime - startTime);
    }
}


 μ΄λŸ¬λ©΄ μ‹€ν–‰μ‹œμ μ— μ „λž΅μ΄ 변경될 λ•Œ, Contextλ₯Ό ꡳ이 λ§Œλ“€μ–΄μ€„ ν•„μš”κ°€ μ—†μ–΄μ§€κ²Œ λ©λ‹ˆλ‹€! μ΄λ ‡κ²Œ 말이죠

@Test
public void strategyTest() throws Exception {
    Strategy strategyLogic1 = new StrategyLogic1();
    Strategy strategyLogic2 = new StrategyLogic2();

    ContextV2 context = new ContextV2();
    context.execute(strategyLogic1);
    context.execute(strategyLogic2);
}


 μ’€ 더 ν΄λ¦°ν•˜κ²Œ 짜면 μ΄λ ‡κ²Œ ν•  수 있죠. 

@Test
public void strategyV4() throws Exception {
    ContextV2 context = new ContextV2();
    context.execute(() -> {
        log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직1 μ‹€ν–‰");
    });
    context.execute(() -> {
        log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직2 μ‹€ν–‰");
    });
}

ν•˜μ§€λ§Œ μ§€κΈˆ μš°λ¦¬κ°€ μ›ν•˜λŠ” 것은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 의쑴 관계λ₯Ό μ„€μ •ν•˜λŠ” 것 처럼 μ„  쑰립 - ν›„ 싀행이 μ•„λ‹ˆλΌ, λ‹¨μˆœνžˆ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  λ•Œ λ³€ν•˜μ§€ μ•ŠλŠ” ν…œν”Œλ¦Ώμ΄ 있고, κ·Έ ν…œν”Œλ¦Ώ μ•ˆμ—μ„œ μ›ν•˜λŠ” λΆ€λΆ„λ§Œ 살짝 λ‹€λ₯Έ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜κ³  싢은 κ²ƒμ΄λ‹ˆ 그런 μ μ—μ„œλŠ” v2κ°€ 더 μ ν•©ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€.


Template-Callback νŒ¨ν„΄

GOF의 Strategy νŒ¨ν„΄μ„ Springμ—μ„œλŠ” Template Callback νŒ¨ν„΄μœΌλ‘œ μΉ­ν•©λ‹ˆλ‹€.

  1. Springμ—μ„œ JdbcTemplate처럼 λ‹€μ–‘ν•œ ν…œν”Œλ¦Ώ μ½œλ°± νŒ¨ν„΄μ΄ μ‚¬μš©λ˜λŠ”데 xxxTemplate이라 μ“°μΈ μ½”λ“œλŠ” ν…œν”Œλ¦Ώ μ½œλ°± νŒ¨ν„΄μ΄ μ μš©λ˜μ—ˆλ‹€κ³  λ΄λ„ λ©λ‹ˆλ‹€.
  2. Strategy νŒ¨ν„΄μ˜ Context -> Template, Strategy -> Callback ( λͺ…μΉ­λ§Œ 닀름 )
callback ν•¨μˆ˜λΌ 함은, λ‹€λ₯Έ μ½”λ“œμ˜ 인수둜 λ„˜κ²¨μ£ΌλŠ” μ‹€ν–‰ κ°€λŠ₯ν•œ μ½”λ“œλ₯Ό λ§ν•©λ‹ˆλ‹€. (* callback을 쑰금 더 μ‰½κ²Œ λ§ν•˜λ©΄ μ½”λ“œκ°€ 호좜(call)은 λ˜λŠ”λ° μ‹€μ œ 싀행은 μ½”λ“œλ₯Ό λ„˜κ²¨μ€€ 곳의 λ’€(back)μ—μ„œ μ‹€ν–‰λœλ‹€λŠ” 뜻)

 

@Slf4j
public class TemplateCallbackTest {

    @Test
    public void callbackTest() throws Exception {

        TimeLogTemplate template = new TimeLogTemplate();
        Callback callback = new Callback() {
            @Override
            public void call() {
                log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰1");
            }
        };
        template.execute(callback);
    }
}

// λ©”μ†Œλ“œκ°€ ν•˜λ‚˜λ©΄ λžŒλ‹€μ‹ 적용 ok.
@Test
void callbackV2() {
TimeLogTemplate template = new TimeLogTemplate();
    template.execute(() -> log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직1 μ‹€ν–‰"));
    template.execute(() -> log.info("λΉ„μ¦ˆλ‹ˆμŠ€ 둜직2 μ‹€ν–‰"));
}

Template-Callback νŒ¨ν„΄ 적용 

* λžŒλ‹€μ‹μœΌλ‘œ μ½”λ“œλ₯Ό κΉ”λ”ν•˜κ²Œ μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

Controller 

@GetMapping("/v5/request")
public String request(@RequestParam String itemId) {
    return template.execute("OrderService.orderItem()", () -> {
        orderService.orderItem(itemId);
        return "ok";
    });
}
  • template.execute(msg, new TraceCallback(){..}) : ν…œν”Œλ¦Ώμ„ μ‹€ν–‰ν•˜λ©΄μ„œ μ½œλ°±μ„ μ „λ‹¬ν•©λ‹ˆλ‹€. 

Service

public void orderItem(String itemId) {
    template.execute("OrderService.orderItem()", () -> {
        orderRepository.save(itemId);
        return null;
    });
}

Repository

public void save(String itemId) {
    template.execute("OrderRepository.save()", () -> {
                if (itemId.equals("ex")) {
                    throw new IllegalStateException("μ˜ˆμ™Έ λ°œμƒ");
                }
                sleep(1000);
                return null;
            }
    );
}

 

<정리> 

  • ν…œν”Œλ¦Ώ 콜백 νŒ¨ν„΄κΉŒμ§€ μ§„ν–‰ν•΄λ³΄λ©΄μ„œ λ³€ν•˜λŠ” μ½”λ“œμ™€ λ³€ν•˜μ§€ μ•ŠλŠ” μ½”λ“œ(곡톡기λŠ₯)λ₯Ό λΆ„λ¦¬ν•΄λ³΄λ©΄μ„œ μ½”λ“œλ₯Ό μ΅œμ ν™” ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.
  • κ³΅ν†΅μ μœΌλ‘œ κ³„μΈ΅μ˜ 원본 μ½”λ“œλ₯Ό μˆ˜μ •ν•΄μ•Ό ν•˜λŠ”κ±΄ λ§ˆμ°¬κ°€μ§€ μ˜€λŠ”λ°μš”, μ§€κΈˆκΉŒμ§€ μ„€λͺ…ν•œ 방식은 μ‹€μ œ μŠ€ν”„λ§ μ•ˆμ—μ„œ 많이 μ‚¬μš©λ˜λŠ” λ°©μ‹μ΄μ§€λ§Œ, μŠ€μΌ€μΌμ΄ 컀질수둝 고쳐야 ν•  원본 μ½”λ“œλ„ λ§Žμ•„μ§€λŠ” 것은 λΆˆλ³€ν•©λ‹ˆλ‹€.
  • λ‹€μŒμ—λŠ” ν”„λ‘μ‹œ νŒ¨ν„΄μ„ μ μš©μ‹œμΌœ 원본 μ½”λ“œλ₯Ό μ†λŒ€μ§€ μ•Šκ³  둜그 좔적기λ₯Ό μ μš©ν•  수 μžˆλŠ” 방법을 μ•Œμ•„λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€. 

 

<참고자료>

 

μŠ€ν”„λ§ 핡심 원리 - κ³ κΈ‰νŽΈ - μΈν”„λŸ° | κ°•μ˜

μŠ€ν”„λ§μ˜ 핡심 원리와 κ³ κΈ‰ κΈ°μˆ λ“€μ„ 깊이있게 ν•™μŠ΅ν•˜κ³ , μŠ€ν”„λ§μ„ μžμ‹ μžˆκ²Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€., 핡심 λ””μžμΈ νŒ¨ν„΄, μ“°λ ˆλ“œ 둜컬, μŠ€ν”„λ§ AOPμŠ€ν”„λ§μ˜ 3가지 핡심 κ³ κΈ‰ κ°œλ… μ΄ν•΄ν•˜κΈ° πŸ“’ μˆ˜κ°•

www.inflearn.com

 

λΈ”λ‘œκ·Έμ˜ 정보

Study Repository

rlaehddnd0422

ν™œλ™ν•˜κΈ°