[디자인패턴] 오퍼레이션 패턴 - 템플릿 메소드 패턴, 상태 패턴, Strategy 패턴

2025. 5. 22. 09:30·공부기록/CS

Operation이란?

Operation은 객체지향 설계에서 클래스의 인스턴스가 외부에 제공하는 서비스의 명세를 의미한다.
즉, 객체가 수행할 수 있는 행위(기능)의 추상적인 정의이다.

 

cf. Method는 Operation의 구체적인 구현을 뜻한다.

     Operation이 '무엇을 할 수 있는가'를 정의한다면, Method는 '어떻게 할 것인가'를 구현하는 셈이다.


Operation Pattern

Operation 중심으로 동작을 정의하거나 위임하는 패턴들을 Operation Pattern이라고 볼 수 있다.
대표적으로 템플릿 메서드 패턴, 상태 패턴, 전략 패턴이 이에 해당한다. 

 

OverView

- 템플릿 메서드 패턴 

  • 알고리즘을 메서드로 구현할 때, 알고리즘의 구조는 상위 클래스에서 정의하고, 일부 단계를 하위 클래스에서 재정의할 수 있도록 한다.
  • 공통적으로 구현해야 할 부분이 많을 때, 이를 상위 클래스에 정의하고,
    달라지는 부분만 하위 클래스에서 오버라이딩하여 유연하게 처리한다.
    → 전체 구조는 같고, 일부 로직만 변경하고 싶을 때 사용한다.
  • 상속을 이용한다.

 

- 상태 패턴 

 

  • 객체의 상태를 클래스로 분리하고, 각 상태에 따라 실행되는 오퍼레이션을 다르게 분배한다.
  • 상태에 따른 분기를 if문이나 switch문으로 처리하면 코드가 복잡해지고 유지보수가 어렵다.
    이 패턴에서는 상태를 클래스로 관리하고, 조건 분기 대신 상태 클래스에 책임을 위임하여 처리한다.
    → 상태 변화에 따라 동작을 유연하게 정의하고 관리하고 싶을 때 사용한다.

 

- Strategy 패턴

 

  • 오퍼레이션의 구현을 인터페이스로 추상화하고, 구현체를 교체 가능하도록 캡슐화한다.
  • 템플릿 메서드 패턴과 유사하게 보일 수 있지만, 전략 패턴은 일부 단계를 바꾸는 것이 아니라
    전체 알고리즘 자체를 교체할 수 있다는 점에서 다르다.
    → 기능 자체는 동일하지만, 구현 방식이 전혀 다른 알고리즘을 선택적으로 사용하고 싶을 때 사용한다.
  • 위임을 이용한다.

1. Template Method Pattern

- 기본 구조 

  • Abstract Class 
    하위 클래스에서 구현해야할 추상 primitive operation을 정의한다.  -> concrete class에서 개별적으로 구현한다.
    기본 뼈대인 template method를 구현하고 있다.
  • Concrete Class
    Abstarct Class를 상속 받아서, abstarct으로 선언되어 있는 primitive operation들을 구현한다.
Template Method
알고리즘의 전체 구조 를 정의하는 메서드로, 상위 클래스에 위치한다.
내부적으로 여러 단계(메서드)를 호출하며, 그 중 일부는 하위 클래스에서 정의한다.
Primitive Method (protected로 선언하며, 이 메서드를 최소화하는게 좋다.)
템플릿 메서드가 호출하는 메서드 중, 하위 클래스에서 구현을 제공해야 하는 메서드이다.
→ 알고리즘 중 변하는 부분이며, 오버라이딩을 통해 상황에 따라 다르게 정의된다.
※ 이 메서드가 객체 생성을 담당한다면 팩토리 메서드 패턴의 형태일 수 있다.

- 의도

  • 알고리즘의 공통 구조는 상위 클래스에서 한 번만 정의하고,
    변하는 부분만 하위 클래스에서 오버라이딩할 수 있도록 한다.
  • 코드의 중복을 줄이고, 알고리즘의 일관성을 유지하면서 유연한 확장을 가능하게 만든다.
  • 하위 클래스가 알고리즘 전체를 마음대로 바꾸지 못하도록 하여, 알고리즘의 제어 흐름을 보호할 수 있다.

- 클래스 다이어그램

 

- Code 예시

//Abstract Class
abstract class CaffeineBeverage {

    // Template Method
    public final void prepareRecipe() {
        boilWater();
        brew();                  // Primitive method
        pourInCup();
        addCondiments();         // Primitive method
    }

    void boilWater() {
        System.out.println("물 끓이는 중");
    }

    void pourInCup() {
        System.out.println("컵에 따르는 중");
    }

    // Primitive Methods — 하위 클래스에 구현을 맡긴다!
    abstract void brew();
    abstract void addCondiments();
}

//Concrete Class
class Coffee extends CaffeineBeverage {

    @Override
    void brew() {
        System.out.println("필터로 커피를 내리는 중");
    }

    @Override
    void addCondiments() {
        System.out.println("설탕과 우유 추가");
    }
}

class Tea extends CaffeineBeverage {

    @Override
    void brew() {
        System.out.println("차를 우려내는 중");
    }

    @Override
    void addCondiments() {
        System.out.println("레몬 추가");
    }
}

//Main Class
public class TemplateMethodTest {
    public static void main(String[] args) {
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
        
        System.out.println("----------");

        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
    }
}

 

 

실행 결과

물 끓이는 중
필터로 커피를 내리는 중
컵에 따르는 중
설탕과 우유 추가
----------
물 끓이는 중
차를 우려내는 중
컵에 따르는 중
레몬 추가

 


2. State Pattern

- 기본 구조

  • Context Class
    현재 상태를 나타내는 Concrete state class 객체의 reference를 갖는다.
    모든 요청을 현재 상태 객체에 위임함으로써, 동작이 상태에 따라 달라지도록 한다.
    상태 전환도 보통 이 클래스 내부에서 일어난다.
  • State Interface (Abstract Class)
    각 상태 클래스에서 공통으로 구현해야 할 오퍼레이션의 인터페이스를 정의한다.
    상태에 따른 동작이 다를 수 있는 메서드를 추상 메서드로 선언한다.
  • Concrete State Class
    State 인터페이스를 구현한 클래스로 실제 상태에 따른 동작을 정의한다.
    상태 전환 로직을 포함할 수 있으며, 상태에 따른 객체의 행위를 모아놓은 거라 단순 함수 집합으로 봐도 된다. 
    → if나 switch 문으로 분기 처리하던 코드를 상태 클래스들로 분산시켜 관리한다. (상태를 클래스로!!)
💡아무래도 한 프로그램의 상태는 하나만 존재해야하니 각 상태 클래스는 Singleton으로 구현하는 것이 일반적이다!

- 의도

  • 객체의 상태가 바뀜에 따라 그 객체의 행동을 바꾸고 싶을 때 사용한다.
    동일한 메서드 호출이라도, 상태에 따라 다른 동작을 하게 만들 수 있다.
    상태에 따른 조건문을 제거하고, 상태별 클래스로 책임을 분산시켜 코드 가독성과 유지보수성을 높인다.
  • 상태 객체를 외부에서 변경 가능하게 하면, 실행 중에 동적으로 객체의 행위를 바꿀 수 있다.

- 클래스 다이어그램

 

- Code 예시

// State 인터페이스
interface TCPState {
    void open(TCPConnection connection);
    void close(TCPConnection connection);
    void listen(TCPConnection connection);
}

// Context 클래스
class TCPConnection {
    private TCPState state;

    public TCPConnection() {
        this.state = TCPClosed.getInstance();
    }

    public void setState(TCPState state) {
        this.state = state;
    }

    public void open() {
        state.open(this);
    }

    public void close() {
        state.close(this);
    }

    public void listen() {
        state.listen(this);
    }
}

// Concrete State - Closed
class TCPClosed implements TCPState {
    private static final TCPClosed instance = new TCPClosed();
    private TCPClosed() {}
    public static TCPClosed getInstance() {
        return instance;
    }

    public void open(TCPConnection connection) {
        System.out.println("Transition from CLOSED to ESTABLISHED");
        connection.setState(TCPEstablished.getInstance());
    }

    public void close(TCPConnection connection) {
        System.out.println("Already in CLOSED state.");
    }

    public void listen(TCPConnection connection) {
        System.out.println("Transition from CLOSED to LISTEN");
        connection.setState(TCPListen.getInstance());
    }
}

// Concrete State - Listen
class TCPListen implements TCPState {
    private static final TCPListen instance = new TCPListen();
    private TCPListen() {}
    public static TCPListen getInstance() {
        return instance;
    }

    public void open(TCPConnection connection) {
        System.out.println("Transition from LISTEN to ESTABLISHED");
        connection.setState(TCPEstablished.getInstance());
    }

    public void close(TCPConnection connection) {
        System.out.println("Transition from LISTEN to CLOSED");
        connection.setState(TCPClosed.getInstance());
    }

    public void listen(TCPConnection connection) {
        System.out.println("Already in LISTEN state.");
    }
}

// Concrete State - Established
class TCPEstablished implements TCPState {
    private static final TCPEstablished instance = new TCPEstablished();
    private TCPEstablished() {}
    public static TCPEstablished getInstance() {
        return instance;
    }

    public void open(TCPConnection connection) {
        System.out.println("Already in ESTABLISHED state.");
    }

    public void close(TCPConnection connection) {
        System.out.println("Transition from ESTABLISHED to CLOSED");
        connection.setState(TCPClosed.getInstance());
    }

    public void listen(TCPConnection connection) {
        System.out.println("Cannot LISTEN while in ESTABLISHED state.");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        TCPConnection connection = new TCPConnection();

        connection.listen();    // CLOSED -> LISTEN
        connection.open();      // LISTEN -> ESTABLISHED
        connection.close();     // ESTABLISHED -> CLOSED
    }
}

3. Stratgey Pattern

- 기본 구조

  • Context Class
    전략(Strategy) 인터페이스를 참조하는 필드를 가진다.
    이 필드를 통해 동적으로 실행할 전략 객체를 교체할 수 있다.
    클라이언트는 전략을 직접 바꾸거나, Context가 내부적으로 전략을 변경할 수 있다.
  • Strategy Interface (Abstract Class)
    알고리즘 군을 정의하는 인터페이스 또는 추상 클래스로, 실행할 오퍼레이션의 메서드를 선언한다.
    여러 구현체가 이 인터페이스를 구현하여 각기 다른 알고리즘을 제공한다.
  • Concrete Strategy Class
    Strategy 인터페이스를 구현하는 실제 알고리즘 클래스들이다.
    각 클래스는 동일한 인터페이스를 따르지만, 알고리즘 구현 내용은 다르다.

💡 전략 패턴은 템플릿 메서드 패턴과 달리 상속 대신 위임을 사용한다.
→ 알고리즘 전체를 캡슐화하고, 필요에 따라 실행 시점에 전략 객체를 교체해 유연성을 극대화한다.

 

- 의도

서로 다른 알고리즘들을 하나의 공통 인터페이스로 묶고,
실행할 알고리즘을 런타임에 동적으로 선택하거나 교체하고 싶을 때 사용한다.
조건문 대신 전략 객체를 교체하여 코드를 깔끔하게 유지하며, 확장에도 유리하다.
전체 알고리즘을 바꾸고 싶을 때 적합하며, 알고리즘 일부만 수정하는 템플릿 메서드 패턴과 구별된다.

 

- 코드 예시

public interface PrintStrategy {
    void print(String text);
}

public class UpperCaseStrategy implements PrintStrategy {
    @Override
    public void print(String text) {
        System.out.println(text.toUpperCase());
    }
}

public class LowerCaseStrategy implements PrintStrategy {
    @Override
    public void print(String text) {
        System.out.println(text.toLowerCase());
    }
}

public class NormalStrategy implements PrintStrategy {
    @Override
    public void print(String text) {
        System.out.println(text);
    }
}

public class TextPrinter {
	//위임을 하기 위한 위임 대상 객체
    private PrintStrategy strategy;

    public TextPrinter(PrintStrategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(PrintStrategy strategy) {
        this.strategy = strategy;
    }

    public void print(String text) {
        // 객체에게 동작을 위임한다.
        strategy.print(text);
    }
}

public class Main {
    public static void main(String[] args) {
        TextPrinter printer = new TextPrinter(new NormalStrategy());
        printer.print("Hello World");  // 기본 출력

        printer.setStrategy(new UpperCaseStrategy());
        printer.print("Hello World");  // HELLO WORLD

        printer.setStrategy(new LowerCaseStrategy());
        printer.print("Hello World");  // hello world
    }
}

 

저작자표시 비영리 변경금지 (새창열림)

'공부기록 > CS' 카테고리의 다른 글

[OS] 프로세스 스케줄링 알고리즘  (0) 2025.05.26
[OS] 프로세스 관리와 CPU 스케줄링  (0) 2025.05.23
[OS] Operating System Intro 2  (0) 2025.05.18
[디자인패턴] 생성패턴 (1) - 빌더 패턴 / Builder Pattern  (0) 2025.05.13
[OS] Operating System Intro 1  (0) 2025.05.12
'공부기록/CS' 카테고리의 다른 글
  • [OS] 프로세스 스케줄링 알고리즘
  • [OS] 프로세스 관리와 CPU 스케줄링
  • [OS] Operating System Intro 2
  • [디자인패턴] 생성패턴 (1) - 빌더 패턴 / Builder Pattern
Lyv
Lyv
  • Lyv
    inimizi
    Lyv
  • 전체
    오늘
    어제
    • 분류 전체보기 (60)
      • 이것저것 도전 (5)
        • 공모전 (0)
        • 우테코 (5)
      • PS (16)
        • 삼성기출 (2)
        • LeetCode & Codility (4)
        • Programmers (6)
        • BaekJoon (4)
      • 공부기록 (33)
        • CS (16)
        • 영어 (1)
        • iOS (1)
        • 프로그래밍 언어 (0)
        • Web (4)
        • Linux (1)
        • Docker (2)
        • Network (4)
        • IaC (3)
      • 프로젝트 경험 (0)
      • DailyLog (4)
      • 취준Log (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    리눅스
    우테코프리코스
    IAC
    manifest
    DP
    컨테이너
    PS
    대학생
    운영체제
    ansible
    공부기록
    정처기실기
    자동화
    네트워크
    c언어
    C++
    til
    백준
    디자인패턴
    문제풀이
    이미지
    운영체제intro
    프리코스회고
    코테
    FastAPI
    스케줄링
    os
    프로그래머스
    우테코
    정처기
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Lyv
[디자인패턴] 오퍼레이션 패턴 - 템플릿 메소드 패턴, 상태 패턴, Strategy 패턴
상단으로

티스토리툴바