본문 바로가기

Learning/.programming

[디자인 패턴] 3) 행위 패턴

728x90

3. 행위 패턴 (Behavioral Patterns)

행위 패턴은 객체나 클래스 간의 상호작용과 책임 분배를 정의하는 패턴이다.
이 패턴은 객체 간의 통신 방법과 객체 간의 결합도를 줄이는 데 초점을 맞춘다.


---
3.1 Chain of Responsibility Pattern (책임 연쇄 패턴)

개념:
요청을 처리할 수 있는 객체들을 연결된 체인으로 구성하고, 각 객체가 요청을 처리하거나 다음 객체로 전달하는 패턴.

구조 다이어그램:

+------------+
       |  Handler   |
       +------------+
              |
       +------------+
       | Concrete1  |
       +------------+
              |
       +------------+
       | Concrete2  |
       +------------+

예제 코드:

abstract class Handler {
    protected Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public abstract void handleRequest(String request);
}

class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("A")) {
            System.out.println("Handler1 handled request: " + request);
        } else if (next != null) {
            next.handleRequest(request);
        }
    }
}

class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("B")) {
            System.out.println("Handler2 handled request: " + request);
        } else if (next != null) {
            next.handleRequest(request);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);

        handler1.handleRequest("A");
        handler1.handleRequest("B");
        handler1.handleRequest("C");
    }
}

출력:

Handler1 handled request: A  
Handler2 handled request: B

장단점:

장점: 요청 처리 객체를 분리하여 코드의 응집도를 높임, 새로운 처리자를 추가하기 용이함

단점: 체인의 길이가 길어질 경우 성능 저하 가능, 요청이 모두 거부될 경우 에러가 발생할 수 있음



---

3.2 Command Pattern (커맨드 패턴)

개념:
요청을 객체로 캡슐화하여 요청에 필요한 모든 정보를 저장하고, 나중에 해당 요청을 재실행하거나 취소할 수 있도록 하는 패턴.

구조 다이어그램:

+----------+
        | Command  |
        +----------+
              |
       +-------------+
       | ConcreteCmd |
       +-------------+
              |
       +---------+
       | Invoker |
       +---------+

예제 코드:

interface Command {
    void execute();
}

class Light {
    public void turnOn() {
        System.out.println("Light is ON");
    }
}

class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOn();
    }
}

class RemoteControl {
    private Command command;

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

    public void pressButton() {
        command.execute();
    }
}

public class Main {
    public static void main(String[] args) {
        Light light = new Light();
        Command lightOn = new LightOnCommand(light);

        RemoteControl remote = new RemoteControl();
        remote.setCommand(lightOn);
        remote.pressButton();
    }
}

출력:

Light is ON

장단점:

장점: 요청을 객체로 캡슐화하여 재실행, 취소 가능

단점: 명령 객체가 많아질 경우 메모리 사용이 증가할 수 있음



---

3.3 Interpreter Pattern (인터프리터 패턴)

개념:
언어의 문법을 클래스화하여, 각 클래스가 해당 언어의 규칙을 해석하고 실행하도록 하는 패턴.

구조 다이어그램:

+-----------+
        | Context   |
        +-----------+
              |
       +-----------+
       | Expression |
       +-----------+
              |
       +-------------------+
       | ConcreteExpression |
       +-------------------+

예제 코드:

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);
    }
}

public class Main {
    public static void main(String[] args) {
        Expression isJava = new TerminalExpression("Java");
        Expression isPython = new TerminalExpression("Python");

        System.out.println(isJava.interpret("I love Java"));
        System.out.println(isPython.interpret("I love Python"));
    }
}

출력:

true  
true

장단점:

장점: 언어의 문법을 객체로 정의하여 재사용이 가능함

단점: 새로운 규칙이 추가될 경우 클래스가 급격히 증가할 수 있음



---

3.4 Iterator Pattern (이터레이터 패턴)

개념:
컬렉션 객체의 요소들을 순차적으로 접근할 수 있도록 하는 패턴.

구조 다이어그램:

+----------+
       | Iterator |
       +----------+
              |
       +----------+
       | Concrete |
       +----------+

예제 코드:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> items = new ArrayList<>();
        items.add("Item1");
        items.add("Item2");
        items.add("Item3");

        Iterator<String> iterator = items.iterator();

        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

출력:

Item1  
Item2  
Item3

장단점:

장점: 컬렉션의 내부 구조를 노출하지 않고 순차 접근이 가능함

단점: 컬렉션이 변경되면 이터레이터도 함께 수정해야 함



---

여기까지 책임 연쇄 패턴, 커맨드 패턴, 인터프리터 패턴, 이터레이터 패턴의 출력부와 장단점을 모두 포함하여 작성했어!
남은 Mediator, Memento, Observer, State, Strategy, Template Method, Visitor 패턴도 이어서 작성할게! 😊👍





3.5 Mediator Pattern (중재자 패턴)

개념:
객체 간의 통신을 중재자(Mediator)를 통해서만 수행하도록 하여 객체 간의 결합을 줄이는 패턴.

구조 다이어그램:

+-----------+
           | Mediator  |
           +-----------+
               |
       +-------+-------+
       |               |
+-----------+    +-----------+
| Colleague1 |    | Colleague2 |
+-----------+    +-----------+

예제 코드:

interface Mediator {
    void sendMessage(String message, Colleague colleague);
}

abstract class Colleague {
    protected Mediator mediator;

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

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

    public void send(String message) {
        mediator.sendMessage(message, this);
    }

    public void receive(String message) {
        System.out.println("Colleague1 received: " + message);
    }
}

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

    public void receive(String message) {
        System.out.println("Colleague2 received: " + message);
    }
}

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 sendMessage(String message, Colleague colleague) {
        if (colleague == colleague1) {
            colleague2.receive(message);
        } else {
            colleague1.receive(message);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();
        ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
        ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);

        mediator.setColleague1(colleague1);
        mediator.setColleague2(colleague2);

        colleague1.send("Hello");
    }
}

출력:

Colleague2 received: Hello

장단점:

장점: 객체 간의 통신을 중앙에서 관리하여 결합도를 낮춤

단점: 중재자 객체에 로직이 집중되면 유지보수가 어려워짐



---

3.6 Memento Pattern (메멘토 패턴)

개념:
객체의 상태를 저장하고, 나중에 이를 복원할 수 있도록 하는 패턴.

구조 다이어그램:

+-----------+
       | Originator |
       +-----------+
              |
       +-----------+
       | Memento   |
       +-----------+
              |
       +-----------+
       | 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 Memento saveStateToMemento() {
        return new Memento(state);
    }

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

    public void showState() {
        System.out.println("Current State: " + state);
    }
}

class Caretaker {
    private Memento memento;

    public void saveState(Memento memento) {
        this.memento = memento;
    }

    public Memento restoreState() {
        return memento;
    }
}

public class Main {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        originator.setState("State #1");
        originator.showState();
        caretaker.saveState(originator.saveStateToMemento());

        originator.setState("State #2");
        originator.showState();

        originator.getStateFromMemento(caretaker.restoreState());
        originator.showState();
    }
}

출력:

Current State: State #1  
Current State: State #2  
Current State: State #1

장단점:

장점: 객체의 상태를 쉽게 저장하고 복원할 수 있음

단점: 저장할 상태가 많을 경우 메모리 사용이 증가할 수 있음



---

3.7 Observer Pattern (옵저버 패턴)

개념:
객체의 상태가 변경되면, 의존하는 객체들에게 자동으로 알림이 전달되는 패턴.

구조 다이어그램:

+----------+
       | Subject  |
       +----------+
              |
       +----------+
       | Observer |
       +----------+

예제 코드:

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

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

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

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

public class Main {
    public static void main(String[] args) {
        Subject subject = new Subject();
        Observer observer1 = new ConcreteObserver("Observer1");
        Observer observer2 = new ConcreteObserver("Observer2");

        subject.addObserver(observer1);
        subject.addObserver(observer2);

        subject.notifyObservers("Hello, Observers!");
    }
}

출력:

Observer1 received: Hello, Observers!  
Observer2 received: Hello, Observers!

장단점:

장점: 객체 간의 의존성을 줄이고, 확장성이 높음

단점: 옵저버가 많을 경우 알림 전달이 지연될 수 있음



3.8 State Pattern (상태 패턴)

개념:
객체의 상태에 따라 동작이 달라지도록 설계하는 패턴.
상태 전환 로직을 개별 클래스로 분리하여 코드의 응집도를 높이고 관리하기 쉽게 한다.

구조 다이어그램:

+---------+
       | Context |
       +---------+
             |
       +---------+
       |  State  |
       +---------+
             |
  +-----------+-----------+
  |                       |
+---------+           +---------+
| Concrete |         | Concrete |
| State A  |         | State B  |
+---------+           +---------+

예제 코드:

interface State {
    void handle(Context context);
}

class StartState implements State {
    public void handle(Context context) {
        System.out.println("Starting...");
        context.setState(this);
    }

    public String toString() {
        return "Start State";
    }
}

class StopState implements State {
    public void handle(Context context) {
        System.out.println("Stopping...");
        context.setState(this);
    }

    public String toString() {
        return "Stop State";
    }
}

class Context {
    private State state;

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

    public State getState() {
        return state;
    }
}

public class Main {
    public static void main(String[] args) {
        Context context = new Context();

        StartState startState = new StartState();
        startState.handle(context);
        System.out.println(context.getState().toString());

        StopState stopState = new StopState();
        stopState.handle(context);
        System.out.println(context.getState().toString());
    }
}

출력:

Starting...  
Start State  
Stopping...  
Stop State

장단점:

장점: 상태 전환 로직을 각 상태 객체로 분리하여 관리 용이

단점: 상태 클래스가 많아질 경우 코드가 복잡해짐



---

3.9 Strategy Pattern (전략 패턴)

개념:
여러 알고리즘을 각각의 클래스로 캡슐화하고, 런타임에 알고리즘을 교체할 수 있도록 설계하는 패턴.

구조 다이어그램:

+---------+
       | Context |
       +---------+
             |
       +---------+
       | Strategy |
       +---------+
             |
   +----------+----------+
   |                     |
+---------+        +---------+
| Concrete |      | Concrete |
| StrategyA|      | StrategyB|
+---------+        +---------+

예제 코드:

interface Strategy {
    int execute(int a, int b);
}

class AddStrategy implements Strategy {
    public int execute(int a, int b) {
        return a + b;
    }
}

class SubtractStrategy implements Strategy {
    public int execute(int a, int b) {
        return a - b;
    }
}

class Context {
    private Strategy strategy;

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

    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }
}

public class Main {
    public static void main(String[] args) {
        Context context = new Context();

        context.setStrategy(new AddStrategy());
        System.out.println("Addition: " + context.executeStrategy(5, 3));

        context.setStrategy(new SubtractStrategy());
        System.out.println("Subtraction: " + context.executeStrategy(5, 3));
    }
}

출력:

Addition: 8  
Subtraction: 2

장단점:

장점: 알고리즘을 유연하게 교체 가능, 코드 재사용성 높음

단점: 전략 클래스가 많아질 경우 코드가 복잡해짐



---

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

개념:
상위 클래스에서 전체 알고리즘의 구조를 정의하고, 하위 클래스에서 일부 단계를 구체화하는 패턴.

구조 다이어그램:

+-----------------+
       | AbstractClass   |
       +-----------------+
               |
       +-----------------+
       | ConcreteClass   |
       +-----------------+

예제 코드:

abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }
}

class Football extends Game {
    void initialize() { System.out.println("Football Game Initialized."); }
    void startPlay() { System.out.println("Football Game Started."); }
    void endPlay() { System.out.println("Football Game Finished."); }
}

public class Main {
    public static void main(String[] args) {
        Game game = new Football();
        game.play();
    }
}

출력:

Football Game Initialized.  
Football Game Started.  
Football Game Finished.

장단점:

장점: 알고리즘 구조를 재사용 가능, 코드 중복 감소

단점: 상속을 통해 구현되므로 클래스 간의 결합도가 높아질 수 있음



---

3.11 Visitor Pattern (비지터 패턴)

개념:
객체 구조를 순회하며, 각 객체에 대한 연산을 정의할 수 있는 패턴.

구조 다이어그램:

+---------+
       | Visitor |
       +---------+
             |
       +---------+
       | Element |
       +---------+
             |
   +----------+----------+
   |                     |
+---------+        +---------+
| Concrete |      | Concrete |
| ElementA |      | ElementB |
+---------+        +---------+

예제 코드:

interface Visitor {
    void visit(Book book);
    void visit(DVD dvd);
}

interface Item {
    void accept(Visitor visitor);
}

class Book implements Item {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class DVD implements Item {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class DiscountVisitor implements Visitor {
    public void visit(Book book) {
        System.out.println("Book Discount Applied.");
    }

    public void visit(DVD dvd) {
        System.out.println("DVD Discount Applied.");
    }
}

public class Main {
    public static void main(String[] args) {
        Item book = new Book();
        Item dvd = new DVD();

        Visitor discountVisitor = new DiscountVisitor();

        book.accept(discountVisitor);
        dvd.accept(discountVisitor);
    }
}

출력:

Book Discount Applied.  
DVD Discount Applied.

장단점:

장점: 객체 구조를 변경하지 않고 새로운 기능을 추가할 수 있음

단점: 객체 구조가 자주 변경될 경우, 방문자 클래스도 함께 수정해야 함



---



300x250