티스토리 뷰

IT 기술

디자인 패턴 - 행동 패턴

cheons 2022. 7. 22. 08:00
728x90
반응형

클래스를 설계할 때, 저장할 데이터와 행위를 추상화합니다. 그리고, 클래스가 담당할 역할 및 책임을 정의합니다. 설계가 완료되면, 클래스와 객체가 어떻게 동작할지 소스코드로 작성합니다. 이때, 소스코드로 객체의 행동을 묘사하기 위해 상속, 다형성 등 객체지향의 특성을 활용하여, 소스코드를 추상화합니다. 이처럼 클래스 또는 객체의 행위를 소스코드를 추상화하는 패턴을 디자인 패턴의 행동 패턴이라고 합니다.

이번 포스팅에서는 디자인 패턴 중 행동 패턴의 책임 연쇄, 커맨드, 인터프리터, 반복자, 옵저버, 전략 패턴에 대해 알아보겠습니다.

행동 패턴 (Behavioral Pattern)

클래스 또는 객체의 책임 분배에 관련된 패턴

책임 연쇄 (Chain of Responsibility)

  • 요청 객체와 처리 객체를 분리한 디자인 패턴
  • 여러 개의 객체 중에서 어떤 것이 요구를 처리할 수 있는지 사전에 알 수 없을 때 사용
  • Sender : Handler에게 처리를 요청하는 클래스
  • Handler : 요청을 처리하기 위한 Receiver를 가지는 인터페이스
  • Receiver : Handler의 인터페이스 구현, 요청 종류에 따라 자신이 처리할 수 있는 부분을 정의하는 클래스
/* Handler */
public abstract class Handler {
	public Handler nextHandler;
	protected int assignNum;

	public Handler setNext(Handler next) {
		Handler h = this;
		while (h.nextHandler != null)
			h = h.nextHandler;
		h.nextHandler = next;
		return this;
	}

	public void handleRequest(String msg, int requestNum) {
		if (assignNum == requestNum) {
			processRequest(msg);
		} else {
			nextHandler.handleRequest(msg, requestNum);
		}
	}
	abstract void processRequest(String msg);
}

/* Receiver1 */
public class Receiver1 extends Handler {
	public Receiver1(int num) {
		this.assignNum = num;
	}
	@Override
	void processRequest(String msg) {
		System.out.println("this \'" + msg + "\' is handled from Receiver 1");
	}
}

/* Receiver2 */
public class Receiver2 extends Handler {
	public Receiver2(int num) {
		this.assignNum = num;
	}

	@Override
	void processRequest(String msg) {
		System.out.println("this \'" + msg + "\' is handled from Receiver 2");
	}
}
/* Sender */
@Test
public void TestChainOfResponsibility()
{
    Handler handler = new Receiver1(1).setNext(new Receiver2(2));
    handler.handleRequest("Please send this message to Receiver1 ", 1);
    handler.handleRequest("Please send this message to Receiver2 ", 2);
}

<Console>
this 'Please send this message to Receiver1 ' is handled from Receiver 1
this 'Please send this message to Receiver2 ' is handled from Receiver 2

커맨드 (Command)

  • 호출부(Invoker)와 실행부(Receiver)를 객체로 캡슐화하여 처리하는 패턴
  • Invoker : 기능의 실행을 호출하는 클래스
  • Receiver : ConcreteCommand에서 구현한 기능을 실행하는 클래스
  • Command : 실행될 기능을 정의한 인터페이스
  • ConcreteCommand : 실행될 기능을 구현한 클래스
/* Receiver */
public interface ISwitchable {
	void PowerOn();
	void PowerOff();
}

/* Receiver (Concrete Receiver) */
public class Light implements ISwitchable {
	@Override
	public void PowerOn() {
		System.out.println("The Light is On");
	}

	@Override
	public void PowerOff() {
		System.out.println("The Light is Off");
	}
}

/* Command */
public interface ICommand {
	void Execute();
}

/* Concrete Command 1/2 */
public class OpenSwitchCommand implements ICommand {
	private ISwitchable _switchable;
	public OpenSwitchCommand(ISwitchable switchable) {
		this._switchable = switchable; 
	}
	@Override
	public void Execute() {
		_switchable.PowerOn();
	}
}

/* Concrete Command 2/2 */
public class CloseSwitchCommand implements ICommand {
	private ISwitchable _switchable;

	public CloseSwitchCommand(ISwitchable switchable) {
		this._switchable = switchable;
	}

	@Override
	public void Execute() {
		_switchable.PowerOff();
	}
}

/* Invoker */
public class Switch {
	ICommand _closedCommand;
	ICommand _openedCommand;

	public Switch(ICommand openedCommand, ICommand closedCommand) {
		this._openedCommand = openedCommand;
        this._closedCommand = closedCommand;
	}

	public void Close() {
		this._closedCommand.Execute();
	}

	public void Open() {
		this._openedCommand.Execute();
	}

}
@Test
public void TestCommand() {
    ISwitchable lamp = new Light();
    ICommand switchClose = new CloseSwitchCommand(lamp);
    ICommand switchOpen = new OpenSwitchCommand(lamp);

    Switch _switch = new Switch(switchOpen, switchClose);

    _switch.Open();
    _switch.Close();
}

<Console>
The Light is On
The Light is Off

인터프리터 (Interpreter)

  • 일련의 규칙을 정의된 문법으로 해석하는 패턴
  • Context : 인터프리터에 보내는 정보, String 표현식
  • Client : interpret()을 호출하는 클래스
  • AbstractExpression : interpret()을 정의하는 인터페이스 또는 추상 클래스
  • TerminalExpression : interpret()을 구현하는 클래스
  • NonTerminalExpression : Non-Terminal의 interpret()을 구현하는 클래스
/* AbstractExpression */
public interface Expression {
	public boolean interpret(String context);
}

/* Terminal Expression */
public class TerminalExpression implements Expression {
	private String data;

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

	@Override
	public boolean interpret(String context) {
		if(context.contains(data))
			return true;
		return false;
	}
}

/* Non-Terminal Expression 1/2 */
public class AndExpression implements Expression{
	private Expression expr1 = null;
	private Expression expr2 = null;
	
	public AndExpression(Expression expr1, Expression expr2) {
		this.expr1 = expr1;
		this.expr2 = expr2;
	}

	@Override
	public boolean interpret(String context) {
		return expr1.interpret(context) && expr2.interpret(context);
	}
}

/* Non-Terminal Expression 2/2 */
public class OrExpression implements Expression {
	private Expression expr1 = null;
	private Expression expr2 = null;

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

	@Override
	public boolean interpret(String context) {
		return expr1.interpret(context) || expr2.interpret(context);
	}
}
@Test
public void TestInterpreter() {
    Expression robert = new TerminalExpression("Robert");
    Expression john = new TerminalExpression("John");

    Expression julie = new TerminalExpression("Julie");
    Expression married = new TerminalExpression("Married");

    Expression isMale = new OrExpression(robert, john);
    Expression isMarriedWoman = new AndExpression(julie, married);
    System.out.println("isMale : " + isMale.interpret("john male"));
    System.out.println("isMarriedWoman : " + isMarriedWoman.interpret("Married Julie"));
}

<Console>
isMale : false
isMarriedWoman : true

반복자 (Iterator)

  • 집합체 안에 들어있는 모든 항목에 접근할 수 있는 방법을 추상화한 패턴
  • Iterator : 집합체의 요소들을 순서대로 검색하기 위한 인터페이스 정의
  • ConcreteIterator : Iterator 인터페이스를 구현함
  • Aggregate : 여러 요소들로 이루어져 있는 집합체
  • ConcreteAggregate : Aggregate 인터페이스를 구현하는 클래스
/* Aggregate */
public interface Aggregate {
	public Iterator iterator();
}

/* Iterator */
public interface Iterator  {
	public boolean hasNext();
	public Object next();
}

/* Concrete Iterator */
public class BookShelfIterator implements Iterator {
	private BookShelf bookShelf;
	private int index;

	public BookShelfIterator(BookShelf bookShelf) {
		this.bookShelf = bookShelf;
		this.index = 0;
	}

	public boolean hasNext() {
		if (index < bookShelf.getLength())
			return true;
		else
			return false;
	}

	@Override
	public Object next() {
		Book book = bookShelf.getBookAt(index);
		index++;
		return book;
	}
}

/* Concrete Aggregate */
public class BookShelf implements Aggregate {
	private Book[] books;
	private int last = 0;

	public BookShelf(int maxSize) {
		this.books = new Book[maxSize];
	}

	public Book getBookAt(int index) {
		return books[index];
	}

	public void appendBook(Book book) {
		this.books[last] = book;
		last++;
	}

	public int getLength() {
		return last;
	}

	public Iterator iterator() {
		return new BookShelfIterator(this);
	}

}

/* Element */
public class Book {
	private String name;

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

	public String getName() {
		return this.name;
	}
}
@Test
public void TestIterator() {
    BookShelf bookShelf = new BookShelf(4);
    bookShelf.appendBook(new Book("Book1"));
    bookShelf.appendBook(new Book("Book2"));
    bookShelf.appendBook(new Book("Book3"));

    Iterator it = bookShelf.iterator();
    while (it.hasNext()) {
        Book book = (Book) it.next();
        System.out.println(book.getName());
    }
}

<Console>
Book1
Book2
Book3

옵저버 (Observer)

  • 관찰 중인 객체에서 발생하는 이벤트를 여러 다른 객체에 알리는 메커니즘을 정의 가능한 패턴
  • Observer : 변경사항을 알릴 내용을 정의한 인터페이스
  • Concretate Observer : 통보받을 변경사항을 구현할 클래스
  • Subject : Observer 대상자를 등록, 해제하고 변경사항을 알리는 클래스
/* Observer */
public interface Observer {
	void notify(String message);
}

/* Concrete Observer 1/2 */
public class ConcreteObserverA implements Observer {
	@Override
	public void notify(String message) {
		System.out.println("ConcreteObserver A is received message : " + message);
		
	}
}

/* Concrete Observer 2/2 */
public class ConcreteObserverB implements Observer {
	@Override
	public void notify(String message) {
		System.out.println("ConcreteObserver B is received message : " + message);
	}
}

/* Subject */
public interface Subject {
	void registerObserver(Observer observer);
	void unregisterObserver(Observer observer);
	void notifyObservers(String message);
}

/* Concrete Subject */
public class ConcreteSubject implements Subject {
	private final List<Observer> observers = new ArrayList<>();

	@Override
	public void registerObserver(Observer observer) {
		observers.add(observer);
	}

	@Override
	public void unregisterObserver(Observer observer) {
		observers.remove(observer);
	}

	@Override
	public void notifyObservers(String message) {
		for (Observer observer : observers) {
			observer.notify(message);
		}
	}
}
@Test
public void TestObserver()
{
    ConcreteObserverA observerA = new ConcreteObserverA();
    ConcreteObserverB observerB = new ConcreteObserverB();
    ConcreteSubject subject = new ConcreteSubject();
    subject.registerObserver(observerA);
    subject.registerObserver(observerB);
    subject.notifyObservers("This message is test");
}

<Console>
ConcreteObserver A is received message : Hello. Everyone
ConcreteObserver B is received message : Hello. Everyone

전략 (Strategy)

  • 행위를 클래스로 캡슐화하여 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴
  • Strategy : 알고리즘을 호출하는 방법을 명시하는 인터페이스
  • ConcreteStrategy : 알고리즘을 구현한 클래스
  • Context : 전략 패턴을 이용하는 클래스
/* Strategy */
public interface Strategy {
	double GetPrice(double rawPrice);
}

/* Concrete Strategy 1/2 */
public class NormalStrategy implements Strategy{
	@Override
	public double GetPrice(double rawPrice) {
		return rawPrice;
	}
}

/* Concrete Strategy 2/2 */
public class SpecialStrategy implements Strategy {
	@Override
	public double GetPrice(double rawPrice) {
		return rawPrice * 0.5;
	}
}

/* Context */
public class Context {
	private Strategy salesStrategy;

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

	public double printPrice(double rawPrice) {
		return salesStrategy.GetPrice(rawPrice);
	}
}
@Test
public void TestStrategy() {
    Context context = new Context(new NormalStrategy());
    System.out.println(context.printPrice(100.0));
    context = new Context(new SpecialStrategy());
    System.out.println(context.printPrice(100.0));
}

<Console>
100.0
50.0

 

728x90
반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함