행동 패턴

2025. 6. 11. 22:23·OOP/GoF

Strategy 패턴

더보기

전략을 객체로 캡슐화해서 실행 중에 갈아 끼울 수 있게 한다.

// 전략 인터페이스
interface FlyBehavior {
	void fly();
}
// 구체 전략들
class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("날개로 납니다!");
    }
}

class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("저는 못 날아요.");
    }
}
// Context 클래스
class Duck {
    private FlyBehavior flyBehavior;

    public Duck(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void performFly() {
        flyBehavior.fly();
    }

    public void setFlyBehavior(FlyBehavior fb) {
        this.flyBehavior = fb;
    }
}
// 사용 예
public class Main {
    public static void main(String[] args) {
        Duck mallard = new Duck(new FlyWithWings());
        mallard.performFly(); // 날개로 납니다!

        // 전략 변경 (런타임)
        mallard.setFlyBehavior(new FlyNoWay());
        mallard.performFly(); // 저는 못 날아요.
    }
}

행동을 클래스로 분리해서 Duck에 전략으로 주입하였다.

State 패턴

더보기

객체의 내부 상태에 따라 행동을 바꾸고 싶을 때,

상태를 객체로 캡슐화해서 상태 전환을 깔끔하게 처리하는 패턴

if-else 문으로 상태마다 다른 행동을 지정하는 대신, 상태를 클래스로 나누고 객체에게 행동을 위임하는 구조

ex)
🔁 객체의 상태에 따라 동작이 자주 바뀔 때 예: 게임 캐릭터의 상태(걷기/뛰기/점프)
🧱 조건문(if-else, switch)가 상태별로 복잡할 때 각 상태를 클래스로 분리해서 간단하게 유지
🧠 상태 전환이 자주 발생하는 시스템 예: 문, 커피머신, TCP 상태, 플레이어 등
// 상태 인터페이스
interface State {
	void handle(Context context);
}
// 상태 구현
class OpenState implements State {
	public void handle(Context context) {
		System.out.println("문이 열려 있습니다. 이제 닫습니다.");
		context.setState(new ClosedState());
	}
}

class ClosedState implements State {
	public void handle(Context context) {
		System.out.println("문이 닫혀 있습니다. 이제 잠급니다.");
		context.setState(new LockedState());
	}
}

class LockedState implements State {
	public void handle(Context context) {
		System.out.println("문이 잠겨 있습니다. 이제 엽니다.");
		context.setState(new OpenState());
	}
}
// Context 클래스
class Context {
	private State state;
	public Context(State initialState) {
	   this.state = initialState;
	}

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

	public void request() {
	    state.handle(this);
	}
}
public class Main {
	public static void main(String[] args) {
		Context door = new Context(new OpenState());
	    door.request(); // 문이 열려 있습니다. 이제 닫습니다.
	    door.request(); // 문이 닫혀 있습니다. 이제 잠급니다.
	    door.request(); // 문이 잠겨 있습니다. 이제 엽니다.
	}
}

객체의 상태가 바뀌면,그 객체의 행동도 바뀌어야 한다

→ 상태를 클래스로 캡슐화하여 동작을 바꾸는 패턴

Command 패턴

더보기

요청(Request)을 캡슐화해서 호출자(Invoker)와 실행자(Receiver)를 분리하는 구조

요청을 객체로 캡슐화해서, 요청을 보낸 쪽과 실제 처리하는 쪽을 분리하고,

나중에 다시 실행하거나, 큐에 저장하거나, 취소할 수 있게 만드는 패턴

//1. 명령(Command) 인터페이스
interface Command {
    void execute();
}
//2. 수신자(Receiver) – 실제 동작을 하는 클래스
class Light {
    public void turnOn() {
        System.out.println("💡 불을 켰습니다.");
    }

    public void turnOff() {
        System.out.println("💡 불을 껐습니다.");
    }
}
//3. 구체 명령(ConcreteCommand)
class LightOnCommand implements Command {
    private Light light;

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

    public void execute() {
        light.turnOn(); // 실제 동작을 Receiver에 위임
    }
}

class LightOffCommand implements Command {
    private Light light;

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

    public void execute() {
        light.turnOff();
    }
}
//4. 호출자(Invoker)
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);
        Command lightOff = new LightOffCommand(light);

        RemoteControl remote = new RemoteControl();

        remote.setCommand(lightOn);
        remote.pressButton(); // 💡 불을 켰습니다.

        remote.setCommand(lightOff);
        remote.pressButton(); // 💡 불을 껐습니다.
    }
}

요청을 객체로 만들어 실행자와 호출자를 분리하는 패턴

→ 버튼, 명령어, 작업 큐, Undo 등에서 강력한 효과 발휘

Chain of Responsibility

더보기

요청을 처리할 수 있는 여러 객체들을 체인(연결된 구조)으로 묶고,

요청을 순차적으로 전달하면서 처리할 수 있는 객체가 처리하게 만드는 행동(Behavioral) 패턴

즉, 누가 처리할지 모르지만 책임을 가진 객체들이 줄지어 대기하고

처리할 수 있는 객체가 나타날 때까지 요청을 넘겨주는 방식

ex)
🔁 요청 처리자가 여러 명 중에 하나일 수 있을 때 예: 이벤트 처리, 로깅, 필터 체인
🔀 요청 처리 순서를 유연하게 바꾸고 싶을 때 순서를 바꿔도 코드 수정이 최소화됨
🧱 클라이언트가 요청을 누가 처리하는지 몰라도 될 때 결합도를 낮출 수 있음
// 요청 처리 인터페이스
abstract class Handler {
    protected Handler next;

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

    public abstract void handle(String request);
}
// 구체 핸들러 A
class AuthHandler extends Handler {
    public void handle(String request) {
        if (request.equals("AUTH")) {
            System.out.println("AuthHandler 처리 완료");
        } else if (next != null) {
            next.handle(request);
        }
    }
}

// 구체 핸들러 B
class LogHandler extends Handler {
    public void handle(String request) {
        if (request.equals("LOG")) {
            System.out.println("LogHandler 처리 완료");
        } else if (next != null) {
            next.handle(request);
        }
    }
}
// 클라이언트
public class Main {
    public static void main(String[] args) {
        Handler handlerChain = new AuthHandler();
        handlerChain.setNext(new LogHandler());

        handlerChain.handle("LOG");   // → LogHandler 처리 완료
        handlerChain.handle("AUTH");  // → AuthHandler 처리 완료
        handlerChain.handle("ETC");   // → 아무도 처리 못 함 (silent fail)
    }
}

Observer 패턴

더보기
//Observer 인터페이스: 관찰자를 나타내는 인터페이스
interface Observer {
	public abstract void update(NumberGenerator generator); 
    //update를 호출하는 것은 숫자를 생성하는 NumberGenerator
    //NumberGenerator가 내용이 갱신되었어요. 표시하는 쪽도 갱신해주세요라고 Observer에 전달하기 위한 메서드
}
//관찰하는 Observer를 저장하는 필드
public abstract class NumberGenerator {
	private List<Observer> observers = new ArrayList<>();

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

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

	//중요! ★Observer에 통지한다.
	public void notifyObserver{
		for(Observer o: observers) {
			o.update(this);		
		}
	}

	public abstract int getNumber();

	public abstract void execute();
}
public class RandomNumberGenerator {
	private Random random = new Random(); //난수 생성기
	private int number; //현재 수

	@Override
	public int getNumber() {
		return number;
	}

	//중요! 수를 생성한다. Observer 전체에 알림
	@Override
	public void execute() {
		for(int i=0; i<20; i++){
			number = random.nextInt(50);
			notifyObservers();
		}
	}
}
public class DigitObserver implements Observer{
	@Override
	public void update(NumberGenerator generator) {
		System.out.println("DigitObserver:" + generator.getNumber());
		try {
			Thread.sleep(100);
		} catch (InterruptedException e){
		}	
	}
}

public class GraphObserver implements Observer {
	@Override
	public void update(NumberGenerator generator) {
		System.out.print("GraphObserver:");
		int count = generator.getNumber();
		for (int i=0; i<count; i++) {
			System.outprint("*");
		}
		System.out.println("");
		try{
			Thread.sleep(100);
		} catch (InterruptedException e) {
		}
	}
}
public class Main{
	public static void main(String[] args) {
    	NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1);
        generator.addObserver(observer2);
        generator.execute();
    }
}

Subject 역에는 여러 Observer가 등록되어 있다. notifyObservers의 메소드에서는 먼저 등록한 Observer의 update 메소드가 호출된다. 

Subject 상태변화 -> Observer로 통지 -> Observer가 Subject의 메소드 호출 ->그에 따라 Subject상태 변화 -> Observer호출

 

MVC에서 Model과 View의 관계는 Observer 패턴의 Subject Observer 관계에 대응된다. Model은 표시 형식에 의존하지 않는 내부모델을 조작하는 부분. 그리고 View는 Model을 어떻게 보여 줄지 관리하는 부분. 일반적으로 하나의 model에 여러 View가 대응된다. 

 

Memento 패턴

더보기

상태 저장/복원을 캡슐화해서 안전하게 구현하는 패턴

→ 내부 구조를 노출하지 않고 save()/restore() 만으로 이전 상태를 복원할 수 있음.

  • 객체의 상태를 저장하고, 필요할 때 복원할 수 있게 해주는 행동 패턴
  • 객체의 내부 상태를 캡슐화해서 저장
  • 외부에서는 객체 내부를 직접 접근하지 않고 상태를 복원
  • 캡슐화 유지가 핵심! (객체의 내부 상태를 외부에서 보지 않고 저장/복원)
// Memento: 저장할 상태 캡슐화
class Memento{
    private final String state;

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

    public String getState(){
        return state;
    }
}
// Originator: 상태를 가진 객체
class TextEditor{
    private String content;

    public void write(String text){
        this.content = text;
    }

    public String read(){
        return content;
    }

    public Memento save(){
        return new Memento(content);
    }

    public void restore(Memento memento){
        this.content = memento.getState();
    }
}
// Caretaker: Memento를 저장하고 관리
class History{
    private Memento backup;

    public void saveBackup(Memento m){
        this.backup = m;
    }

    public Memento getBackup(){
        return backup;
    }
}
// 사용 예
public class Main {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        History history = new History();

        editor.write("초안입니다");
        history.saveBackup(editor.save()); // 상태 저장

        editor.write("실수로 바꿨음");       // 상태 변경
        System.out.println(editor.read()); // → 실수로 바꿨음

        editor.restore(history.getBackup()); // 복원
        System.out.println(editor.read()); // → 초안입니다
    }
}

 

'OOP > GoF' 카테고리의 다른 글

구조 패턴  (0) 2025.06.11
생성 패턴  (0) 2025.05.30
'OOP/GoF' 카테고리의 다른 글
  • 구조 패턴
  • 생성 패턴
dev.di
dev.di
devdi 님의 블로그 입니다.
  • dev.di
    개발 블로그
    dev.di
  • 전체
    오늘
    어제
    • 분류 전체보기 (28)
      • Algorithm (9)
        • Basics (9)
      • AWS (0)
        • AWS (0)
        • SAA (0)
      • Computer Science (1)
        • OS 벼락치기 (1)
        • DB 벼락치기 (0)
      • Data Engineer (8)
        • Airflow (0)
        • Data Warehouse (0)
        • Kafka (0)
        • Spark (0)
        • 데브코스 (8)
      • Docker (0)
      • Interviews (1)
      • Network (2)
        • Physical Layer (0)
        • Data Link Layer (0)
      • OOP (3)
        • GoF (3)
      • Python (4)
        • Django (3)
        • Scraping (1)
      • Software Engineering (0)
      • Spring (0)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

    IPv4
    데이터 웨어하우스
    sql
    포트포워딩
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
dev.di
행동 패턴
상단으로

티스토리툴바