[디자인패턴] 행위 패턴 (Behavioral Patterns)

엔스마트·2024년 6월 26일

디자인 패턴

목록 보기
4/4

행위 패턴(Behavioral Patterns)은 객체나 클래스 사이의 상호작용과 책임 분배를 다루는 디자인 패턴으로, 시스템의 복잡한 흐름을 관리합니다. 행위 패턴은 알고리즘, 책임 할당, 객체 간의 통신을 쉽게 구현할 수 있게 해줍니다. 아래에서는 주요 행위 패턴들을 상세히 분석하겠습니다.

1. 옵저버 패턴 (Observer Pattern)

  • 목적: 객체의 상태 변화에 따라 다른 객체들이 통지받고 자동으로 갱신되도록 합니다.
  • 구현:
    • 주제(Subject) 클래스는 옵저버를 등록하고 통지하는 메서드를 가짐.
    • 옵저버(Observer) 클래스는 주제로부터 상태 변경을 통지받는 메서드를 가짐.
import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String state);
}

class ConcreteObserver implements Observer {
    private String observerState;

    public void update(String state) {
        observerState = state;
        System.out.println("Observer state updated to: " + observerState);
    }
}

class Subject {
    private List<Observer> observers = new ArrayList<>();
    private String state;

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }

    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }
}
  • 활용 예시: GUI 이벤트 처리, 모델-뷰-컨트롤러(MVC) 아키텍처

2. 스테이트 패턴 (State Pattern)

  • 목적: 객체의 상태에 따라 행동을 다르게 정의합니다.
  • 구현:
    • 상태(State) 인터페이스와 구체적인 상태 클래스를 정의.
    • 컨텍스트(Context) 클래스는 현재 상태를 포함하고, 상태 전환 메서드를 가짐.
interface State {
    void handle(Context context);
}

class ConcreteStateA implements State {
    public void handle(Context context) {
        System.out.println("State A handling request.");
        context.setState(new ConcreteStateB());
    }
}

class ConcreteStateB implements State {
    public void handle(Context context) {
        System.out.println("State B handling request.");
        context.setState(new ConcreteStateA());
    }
}

class Context {
    private State state;

    public Context(State state) {
        this.state = state;
    }

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

    public void request() {
        state.handle(this);
    }
}
  • 활용 예시: UI 상태 관리, 게임 캐릭터 상태 전환

3. 스트래티지 패턴 (Strategy Pattern)

  • 목적: 알고리즘군을 정의하고 캡슐화하여 교환 가능하게 합니다.
  • 구현:
    • 전략(Strategy) 인터페이스와 구체적인 전략 클래스를 정의.
    • 컨텍스트(Context) 클래스는 전략 인터페이스를 포함하고, 실행 메서드를 가짐.
interface Strategy {
    void execute();
}

class ConcreteStrategyA implements Strategy {
    public void execute() {
        System.out.println("Executing strategy A");
    }
}

class ConcreteStrategyB implements Strategy {
    public void execute() {
        System.out.println("Executing strategy B");
    }
}

class Context {
    private Strategy strategy;

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

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

    public void executeStrategy() {
        strategy.execute();
    }
}
  • 활용 예시: 데이터 정렬 알고리즘, 경로 찾기 알고리즘

4. 템플릿 메서드 패턴 (Template Method Pattern)

  • 목적: 알고리즘의 뼈대를 정의하고, 하위 클래스에서 구체적인 단계를 정의합니다.
  • 구현:
    • 추상 클래스에 템플릿 메서드를 정의하고, 세부 단계를 추상 메서드로 선언.
    • 하위 클래스에서 세부 단계를 구현.
abstract class AbstractClass {
    public final void templateMethod() {
        primitiveOperation1();
        primitiveOperation2();
        concreteOperation();
    }

    protected abstract void primitiveOperation1();
    protected abstract void primitiveOperation2();

    private void concreteOperation() {
        System.out.println("Concrete operation");
    }
}

class ConcreteClass extends AbstractClass {
    protected void primitiveOperation1() {
        System.out.println("Primitive operation 1");
    }

    protected void primitiveOperation2() {
        System.out.println("Primitive operation 2");
    }
}
  • 활용 예시: 게임 캐릭터의 행동 정의, 서브클래스에서 다른 방식으로 실행되는 알고리즘

5. 커맨드 패턴 (Command Pattern)

  • 목적: 요청을 객체로 캡슐화하여 호출자와 수신자를 분리합니다.
  • 구현:
    • 명령(Command) 인터페이스와 구체적인 명령 클래스를 정의.
    • 인보커(Invoker) 클래스는 명령 객체를 실행.
    • 리시버(Receiver) 클래스는 실제 작업을 수행.
interface Command {
    void execute();
}

class ConcreteCommand implements Command {
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.action();
    }
}

class Receiver {
    public void action() {
        System.out.println("Receiver action executed");
    }
}

class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        command.execute();
    }
}
  • 활용 예시: UI 버튼 클릭 이벤트 처리, 작업 취소/재실행 기능

6. 이터레이터 패턴 (Iterator Pattern)

  • 목적: 집합 객체 요소에 순차적으로 접근할 수 있는 방법을 제공합니다.
  • 구현:
    • 이터레이터(Iterator) 인터페이스와 구체적인 이터레이터 클래스를 정의.
    • 집합 객체(Aggregate) 인터페이스와 구체적인 집합 객체 클래스를 정의.
import java.util.ArrayList;
import java.util.List;

interface Iterator<T> {
    boolean hasNext();
    T next();
}

class ConcreteIterator<T> implements Iterator<T> {
    private List<T> collection;
    private int position = 0;

    public ConcreteIterator(List<T> collection) {
        this.collection = collection;
    }

    public boolean hasNext() {
        return position < collection.size();
    }

    public T next() {
        if (this.hasNext()) {
            return collection.get(position++);
        }
        return null;
    }
}

interface Aggregate<T> {
    Iterator<T> createIterator();
}

class ConcreteAggregate<T> implements Aggregate<T> {
    private List<T> collection = new ArrayList<>();

    public void add(T item) {
        collection.add(item);
    }

    public Iterator<T> createIterator() {
        return new ConcreteIterator<>(collection);
    }
}
  • 활용 예시: 컬렉션 프레임워크에서 요소 순회, 파일 시스템에서 파일 탐색

7. 메디에이터 패턴 (Mediator Pattern)

  • 목적: 객체 간의 상호작용을 중앙 집중식으로 관리합니다.
  • 구현:
    • 중재자(Mediator) 인터페이스와 구체적인 중재자 클래스를 정의.
    • 동료(Colleague) 클래스는 중재자를 통해 상호작용.
interface Mediator {
    void notify(Object sender, String event);
}

class ConcreteMediator implements Mediator {
    private ConcreteColleague1 colleague1;
    private ConcreteColleague2 colleague2;

    public void setColleague1(ConcreteColleague1 colleague1) {
        this.colleague1 = colleague1;
    }

    public void setColleague2(ConcreteColleague2 colleague2) {
        this.colleague2 = colleague2;
    }

    public void notify(Object sender, String event) {
        if (event.equals("A")) {
            colleague2.doSomething();
        } else if (event.equals("B")) {
            colleague1.doSomething();
        }
    }
}

class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }
}

class ConcreteColleague1 extends Colleague {
    public ConcreteColleague1(Mediator mediator) {
        super(mediator);
    }

    public void doSomething() {
        System.out.println("ConcreteColleague1 does something");
    }

    public void triggerEvent() {
        mediator.notify(this, "A");
    }
}

class ConcreteColleague2 extends Colleague {
    public ConcreteColleague2(Mediator mediator) {
        super(mediator);
    }

    public void doSomething() {
        System.out.println("ConcreteColleague2 does something");
    }

    public void triggerEvent() {
        mediator.notify(this, "B");
    }
}
  • 활용 예시: 채팅 시스템에서 사용자 간의 메시지 전달, UI 컴포넌트 간의 상호작용 관리

8. 메멘토 패턴 (Memento Pattern)

  • 목적: 객체의 상태를 저장하고 복원할 수 있게 합니다.
  • 구현:
    • 메멘토(Memento) 클래스를 통해 객체 상태를 저장.
    • 오리진(Originator) 클래스는 상태 저장 및 복원 메서드를 가짐.
    • 케어테이커(Caretaker) 클래스는 메멘토 객체를 관리.
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

class Originator {
    private String state;

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

    public String getState() {
        return state;
    }

    public Memento saveStateToMemento() {
        return new Memento(state);
    }

    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

class Caretaker {
    private List<Memento> mementoList = new ArrayList<>();

    public void add(Memento state) {
        mementoList.add(state);
    }

    public Memento get(int index) {
        return mementoList.get(index);
    }
}
  • 활용 예시: 텍스트 편집기의 상태 저장 및 복원, 객체의 이전 상태 복원

9. 체인 오브 리스폰서빌리티 패턴 (Chain of Responsibility Pattern)

  • 목적: 요청을 처리할 기회를 여러 객체에 부여합니다.
  • 구현:
    • 핸들러(Handler) 인터페이스와 구체적인 핸들러 클래스를 정의.
    • 각 핸들러는 다음 핸들러에 요청을 전달할 수 있는 메서드를 가짐.
abstract class Handler {
    protected Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public abstract void handleRequest(String request);
}

class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("Request1")) {
            System.out.println("ConcreteHandler1 handled request");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("Request2")) {
            System.out.println("ConcreteHandler2 handled request");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}
  • 활용 예시: 이벤트 처리 시스템, 요청을 순차적으로 처리하는 책임 연쇄

10. 비지터 패턴 (Visitor Pattern)

  • 목적: 객체 구조에 새로운 기능을 추가합니다.
  • 구현:
    • 비지터(Visitor) 인터페이스와 구체적인 비지터 클래스를 정의.
    • 요소(Element) 인터페이스와 구체적인 요소 클래스를 정의. 각 요소 클래스는 비지터를 받아들이는 메서드를 가짐.
interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}

class ConcreteVisitor implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("Visiting ConcreteElementA");
    }

    public void visit(ConcreteElementB element) {
        System.out.println("Visiting ConcreteElementB");
    }
}

interface Element {
    void accept(Visitor visitor);
}

class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class ConcreteElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
  • 활용 예시: 객체 구조를 순회하며 작업 수행, 컴파일러에서 문법 트리 처리

11. 인터프리터 패턴 (Interpreter Pattern)

  • 목적: 언어의 문법을 정의하고 해석합니다.
  • 구현:
    • 표현(Expression) 인터페이스와 구체적인 표현 클래스를 정의.
    • 컨텍스트(Context) 클래스는 해석에 필요한 정보를 포함.
interface Expression {
    boolean interpret(String context);
}

class TerminalExpression implements Expression {
    private String data;

    public TerminalExpression(String data) {
        this.data = data;
    }

    public boolean interpret(String context) {
        return context.contains(data);
    }
}

class OrExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public OrExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    public boolean interpret(String context) {
        return expr1.interpret(context) || expr2.interpret(context);
    }
}

class AndExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public AndExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    public boolean interpret(String context) {
        return expr1.interpret(context) && expr2.interpret(context);
    }
}
  • 활용 예시: SQL 파서, 정규 표현식 해석기

행위 패턴의 장점과 단점

장점

  • 책임 분리: 각 객체나 클래스의 책임을 명확히 분리하여 코드의 응집력을 높입니다.
  • 유연성: 알고리즘이나 행위를 객체화하여 쉽게 교체하고 확장할 수 있습니다.
  • 재사용성: 공통 행위를 캡슐화하여 재사용할 수 있습니다.

단점

  • 복잡성 증가: 패턴 적용으로 인해 클래스나 인터페이스 수가 증가하여 코드 복잡성이 높아질 수 있습니다.
  • 성능 문제: 객체 간의 상호작용이 많아지면서 성능에 영향을 미칠 수 있습니다.

행위 패턴은 객체 간의 상호작용을 효율적으로 관리하고, 코드의 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 각 패턴을 적절히 활용하여 시스템의 복잡성을 관리하고 유지 보수성을 향상시키는 것이 중요합니다.

profile
클라우드 전환, MSA 서비스, DevOps 환경 구축과 기술지원 그리고 엔터프라이즈 시스템을 구축하는 최고 실력과 경험을 가진 Architect Group 입니다.

0개의 댓글