객체 지향 프로그래밍/디자인 패턴

전략 패턴, 템플릿 콜백 패턴

ghtis1798 2021. 1. 2. 12:07

1. 전략 패턴

전략, 템플릿 콜백 패턴

1.1. 전략 패턴이란?

 

전략 패턴은 프로그램 수행 중에 알고리즘을 선택할 수 있도록 만든 패턴입니다.

선택할 알고리즘이 바로 '전략(Strategy)'이 되는것이죠. 👏

그리고 이 전략을 선택하는 주체인 '컨텍스트(Context)'가 필요합니다.

Context는 문맥이란 뜻인데요.

아마도 맥락에 따라 서로 다른 전략을 선택하기 때문에 붙은 이름이 아닐까 싶습니다. 👀

Context는 전략을 수행하는 객체라고 봐도 무방할 것 같네요.

마지막으로 Context에 Strategy를 주입해주는 '클라이언트(Client)'가 필요합니다.

Context가 직접 전략을 선택하지 못하기 때문이죠.

따라서 클라이언트가 Context에게 문맥에 맞는 Strategy를 '주입(DI:Dependancy Injection)'해 주어야합니다.

전체적인 그림을 보면 전략 패턴은 Context로부터 Strategy를 분리해낸 느낌이에요.

Strategy를 주입하는 Client를 사용해서 말이죠.

다음은 예시를 직접 보겠습니다.

 

1.2. 전략 패턴 예시

 

예를 들어 학생이 선생님께 공부를 배우고 있다고 해보겠습니다.

학생은 빨간색, 파란색, 검은색 펜이 있지만 필기 하는 방법은 모르는 상태입니다.

따라서 선생님이 학생에서 특정 문맥마다 펜을 골라주는 상황입니다.

이것을 UML 다이어그램으로 나타내 보겠습니다.

Strategy Pattern UML Diagram

펜을 고르는 전략은 Strategy, 전략을 수행하는 Student,

컨텍스트에게 전략을 주입하는 Teacher로 구성되어있습니다.

선생님이 학생에게 문맥에 적절한 펜을 골라주는 구조입니다.

코드로 구현해보겠습니다.

전략 패턴 패키지 구조

패키지엔 전략의 개수만큼 추가 클래스를 생성해주었습니다.

package exStrategyPattern;

// 전략 클래스
public interface Strategy {
    // 구현을 강제할 추상 메소드
    public abstract void ChoosePen();
}
package exStrategyPattern;

// 검은펜 전략
public class BlackPen implements Strategy {
    public void ChoosePen() {
        System.out.println("검은펜을 잡았습니다.");
    }
}
package exStrategyPattern;

// 파란펜 전략
public class BluePen implements Strategy {
    public void ChoosePen() {
        System.out.println("파란펜을 잡았습니다.");
    }
}
package exStrategyPattern;

// 빨간펜 전략
public class RedPen implements Strategy{
    public void ChoosePen() {
        System.out.println("빨간펜을 잡았습니다.");
    }
}
package exStrategyPattern;

// Client가 준 전략을 수행하는 Context 클래스
public class Student {
    public void takeNotes(Strategy strategy) {
        System.out.println("=== 선생님이 펜을 주십니다. ===");
        strategy.ChoosePen();
        System.out.println("필기를 시작합니다.");
    }
}
package exStrategyPattern;

public class Teacher {
    public static void main(String[] args) {
        // Context 객체 생성
        Student student = new Student();

        // Strategy 객체 생성
        BlackPen black = new BlackPen();
        RedPen red = new RedPen();
        BluePen blue = new BluePen();

        // Context에 Strategy '주입'
        student.takeNotes(black);
        student.takeNotes(red);
        student.takeNotes(blue);
    }
}

Strategy Pattern code 실행 결과

전략 패턴은 Client가 '객체를 주입하는 행위(DI)'에 주목할 필요가 있습니다.

템플릿 메소드 패턴의 경우 공통 역할은 상위 클래스에 정의하고 나머지는 '상속'을 통해 구현했었습니다.

전략 패턴은 상속 대신 '객체 주입'을 사용했습니다.

그럼 어떤 장점이 있을까요?

우선 전략을 선택적으로 구현할 수 있는 편리함이 있습니다.

원래는 if 조건문으로 구현했어야 할 것을 대체할 수도 있죠.

하지만 사용자가 각각의 전략에 대해 이해하고 알아야한다는 단점도 존재합니다.

 

2. 템플릿 콜백 패턴

 

2.1. 템플릿 콜백 패턴이란?

 

템플릿 콜백 패턴은 전략 패턴 + 익명 내부 클래스입니다. ✨

내부 클래스는 클래스 안에 생성된 클래스입니다.

익명 클래스는 이름이 없는 클래스로 선언과 객체 생성이 동시에 됩니다.

전략 패턴에서 정의했던 클래스들을 Client나 Context에 포함시켜 버린 것이죠.

위에서 필기법을 배우는 학생 예제 코드를 템플릿 콜백 패턴으로 변형해 보겠습니다.

 

2.2. 템플릿 콜백 패턴 예시

 

예시 1

우선 전략 패턴의 구현 내용을 Client에 포함시켜봤습니다.

Strategy 인터페이스와 Student 클래스는 동일하고 Teacher 클래스만 변경합니다.

템플릿 콜백 패턴 패키지 구조

전략 인터페이스와 컨텍스트, 클라이언트만 있으면 됩니다.

전략들을 익명 클래스로 Teacher나 Student에 바로 구현할 것이기 때문이죠.

전략이 추가되더라도 변하지 않아요. 😊

package exTempleteCallBack;
// 전략 패턴과 동일
public interface Strategy {
    public abstract void ChoosePen();
}
package exTempleteCallBack;
// 전략 패턴과 동일
public class Student {
    public void takeNotes(Strategy strategy) {
        System.out.println("=== 선생님이 펜을 주십니다. ===");
        strategy.ChoosePen();
        System.out.println("필기를 시작합니다.");
    }
}
package exTempleteCallBack;

public class Teacher {
    public static void main(String[] args) {
        // Context 객체 생성
        Student student = new Student();

        // Context에 '익명 내부 클래스'로 Strategy 주입
        student.takeNotes(new Strategy() {
            public void ChoosePen() {
                System.out.println("검은펜을 잡았습니다.");
            }
        });

        student.takeNotes(new Strategy() {
            public void ChoosePen() {
                System.out.println("빨간펜을 잡았습니다.");
            }
        });

        student.takeNotes(new Strategy() {
            public void ChoosePen() {
                System.out.println("파란펜을 잡았습니다.");
            }
        });
    }
}

템플릿 콜백 패턴 결과

결과도 물론 같습니다. 😊

예시2

이번엔 전략 패턴의 구현 내용을 Student에 포함시켜 중복을 최소화 해보겠습니다.

외부 동작은 바꾸지 않으면서 내부 구조를 개선하는 것을 '리팩토링'이라고 합니다.

위의 예제를 리팩토링된 템플릿 콜백 패턴으로 적용해보겠습니다.

Strategy 인터페이스는 동일하고 Student와 Client만 조금 바꾸면 됩니다.

package exTempleteCallBack;
// 위와 동일
public interface Strategy {
    public abstract void ChoosePen();
}
package exTemplateCallBack_Refactoring;

public class Student {
    public void takeNotes(String Pen) {
        System.out.println("=== 선생님께서 펜을 주십니다. ===");
        // 변경된 부분
        takePen(Pen).ChoosePen();
        System.out.println("필기를 시작합니다.");
    }

    // Strategy를 익명 클래스로 반환하는 메서드
    private Strategy takePen(String Pen) {
        // 익명 클래스 사용
        return new Strategy() {
            @Override
            public void ChoosePen() {
                System.out.println(Pen + "을 잡았습니다.");
            }
        };
    }
}
package exTemplateCallBack_Refactoring;

public class Teacher {
    public static void main(String[] args) {
        Student student = new Student();

        student.takeNotes("검정펜");
        student.takeNotes("빨간펜");
        student.takeNotes("파란펜");
        student.takeNotes("무지개 형관펜");
    }
}

리팩토링된 템플릿 콜백 패턴

템플릿 콜백 패턴을 리팩토링한 결과,

이제 매개변수로 '무지개 형관펜'이라는 문자열만 넘겨주면 바로 사용하는 것이 가능해졌습니다.

익명 클래스를 사용했기 때문이죠.

코드 구조가 굉장히 깔끔해졌습니다. 👏👏

 

3. 정리 및 느낀점

 

전략 패턴은 객체 주입(DI)를 이용해 if 조건문 사용을 줄일 수 있었습니다.

필요에 의해 전략을 선택적으로 구현할 수 도 있었고요.

템플릿 콜백 패턴은 전략 패턴에 익명 내부 클래스를 결합한 것이었죠.

전략 패턴의 전략들을 익명 클래스로 구현했었습니다.

그 결과 중복을 줄이면서 코드가 깔끔해지는 장점이 있었습니다. 👍 

디자인 패턴을 공부하면서 좋은 코드에 대한 고민 자주 했던 것 같습니다. 🤔

제가 느낀 좋은 코드는 재사용성이 뛰어나고 유지보수가 용이하며,

비슷한 기능끼리는 객체 단위로 묶고, 객체간 영향력은 낮추는 코드였습니다.

이 과정에서 객체간 '응집도와 결합도'라는 개념에 대해서도 배웠습니다.

이를 구현하기 위한 방법들이 '상속', '인터페이스', '객체 합성(Composition)'들이 있었고요.

이제 배웠던 것들을 어떻게 사용할 수 있을지 고민해 봐야겠습니다. 🙂

이상으로 디자인 패턴의 꽃이라 할 수 있는 전략 패턴과 템플릿 콜백 패턴에 대해 정리해보았습니다. 😁😁

 

참고자료

victorydntmd.tistory.com/292

 

[디자인패턴] 전략 패턴 ( Strategy Pattern )

전략 패턴 ( Strategy Pattern ) 객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화 하는 인터페이스를 정의하여, 객체의 행위를 동적으로 바꾸고 싶은 경우

victorydntmd.tistory.com

velog.io/@kyle/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80

 

디자인 패턴 : 전략패턴이란?

디자인 패턴에서 전략패턴이란 무엇인가?

velog.io

os94.tistory.com/128

 

Strategy Pattern (전략 패턴)

Strategy Pattern (전략 패턴) 특정 컨텍스트(Context)에서 알고리즘, 전략(Strategy)을 별도로 분리하는 설계 방법 예시 ) 과일 매장의 할인 정책 한 과일 매장은 현재 2가지의 할인 정책을 열고 있다. 매장

os94.tistory.com

limkydev.tistory.com/85

 

[Design_Pattern] 탬플릿 콜백 패턴(Template Callback Pattern)

안녕하세요. Limky 입니다. 이번 시간엔 Strategy 전략 패턴에 이어 탬플릿 콜백 패턴(Template Callback Pattern)을 알아보겠습니다. 탬플릿 콜백 패턴(Template Callback Pattern)은 전략 패턴의 변형으로 DI..

limkydev.tistory.com

withseungryu.tistory.com/89

 

[Spring] 템플릿 콜백 패턴

💡 템플릿 콜백 패턴이란? 전략 패턴의 기본 구조에 인터페이스를 상속하는 클래스를 만들지 않고 익명 내부 클래스를 활용하는 방식 이런 방식을 스프링에서는 템플릿 콜백 패턴이라고 한다.

withseungryu.tistory.com

pridiot.tistory.com/52?category=869931

 

[Java] 내부 클래스(Inner Class)와 익명 클래스(Anonymous class)

공부했던 자료 정리하는 용도입니다. 재배포, 수정하지 마세요. 내부 클래스(Inner Class)  내부 클래스는 클래스 내에 선언된다는 점을 제외하고는 일반적인 클래스와 다르지 않다. AWT나 Swing과 같

pridiot.tistory.com

scvgoe.github.io/2018-12-25-GoF%EC%9D%98-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-(Summary)-10.-%EC%A0%84%EB%9E%B5(Strategy)/

 

GoF의 디자인 패턴 (Summary) - 10. 전략(Strategy)

의도 동일 계열의 알고리즘군을 정의하고, 각 알고리즘을 캡슐화하며, 이들을 상호교환이 가능하도록 만듭니다. 알고리즘을 사용하는 클라이언트와 상괍없이 독립적으로 알고리즘을 다양하게

scvgoe.github.io

김종민 저, 스프링 입문을 위한 자바 객체 지향의 원리와 이해