박은종의 객체지향 설계를 위한 디자인패턴 with 자바
조건에 따른 여러가지 상태를 클래스로 표현하여 관리하는 패턴(State, Observer, Memento)
상태를 관리
State
- 객체의 기능은 상태에 따라 달라질 수 있는데, 이러한 상태가 여러가지이고, 클래스 전반의 모든 기능이 상태에 의존적이라 하면, 상태를 클래스로 표현하는 것이 적절함
- 클래스로 분리하지 않게 되면 상태가 여러가지인 경우 많은 if-else 문이 사용되고 추후 상태가 추가되거나 삭제될 때 수정해야 하는 사항이 너무 많아짐
- Context : ConcreteState의 인스턴스를 관리하고 서로 상태가 바뀌는 순간을 구현할 수 있다.
- State : Context 가 사용할 메서드를 선언한다.
- ConcreateState : 각 상태 클래스가 수행할 State에 선언된 메서드를 구현한다.
- 상태에 따른 기능을 분리하여 구현
- 새로운 상태가 추가되면 새로운 클래스를 추가한다.
- 각 상태의 switch를 명확하게 구현해 함
(template 패턴 참조)
Observer
- 하나의 객체에 연동되는 여러 객체 집합이 있을 때 변화에 대한 일관성은 유지하고, 객체간의 결합도는 낮게하기 위한 패턴
- 변화에 관심이 있는 객체에 대한 가정없이 통보될 수 있도록 해야 함
- 주로 data - view 의 관계에서 사용됨
- log와 그 handler들의 관계. (file, console, 등등)
package com.backend.bakckend.designpattern.state;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
interface Observer {
abstract void update(NumberGenerator generator);
}
abstract class NumberGenerator{
private List<Observer> observers = new ArrayList<Observer>();
void addObserver(Observer observer) {
observers.add(observer);
}
void deleteObserver(Observer observer) {
observers.remove(observer);
}
void notifyObservers() {
Iterator<Observer> it = observers.iterator();
while (it.hasNext()) {
Observer o = it.next();
o.update(this);
}
}
abstract int getNumber(); // 숫자를 가져옴
abstract void execute(); // 숫자를 생성하고 Observer에 통지
}
class RandomNumberGenerator extends NumberGenerator{
private Random random = new Random();
private int number;
@Override
int getNumber() {
return number;
}
@Override
void execute() {
for (int i = 0; i < 20; i++) {
number = (int) (Math.random() * 50);
notifyObservers();
}
}
}
class DigitObserver implements Observer{
@Override
public void update(NumberGenerator generator) {
System.out.println("DigitObserver:" + generator.getNumber());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
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.out.print("*");
}
System.out.println("");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ObserverTest {
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();
}
}
Momento
- 내부 상태를 객체화하여, 나중에 객체가 이 상태로 복구 가능하게 함
- 인스턴스의 상태를 보존해 두었다가 보존해 둔 정보를 가지고 인스턴스를 원래 상태로 복원
- 인스턴스를 복원하기 위해서는 내부 정보에 자유롭게 접근 가능해야 함
- 캡슐화가 파괴가 일어나지 않도록 주의해야 함
- 이전의 상태로 되돌리는 undo
- 했던 작업을 다시 하는 redo
- 기억해야 하는 순간을 저장하는 객체
- 오류를 복구하거나 수행 결과를 취소하기 위한 작업에 사용
- Memento : Originator 객체의 내부 상태를 필요한 만큼 저장한다. Originator만이 Memento에 접근할 수 있다.
- Originator : Memento를 생성하여 현재 객체의 상태를 저장하고 내부 상태를 복구
- CareTaker (undo mechanism) : Memento의 보관을 책임지기는 하지만, memento의 내부를 확인할 수 없음
package com.backend.bakckend.designpattern.state;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
class Gamer{
private int money;
private ArrayList<String> fruits = new ArrayList<String>();
private Random random = new Random();
private static String[] fruitsname = {
"사과", "포도", "바나나", "귤"
};
public Gamer(int money){
this.money = money;
}
public int getMoney(){
return money;
}
public void bet() {
int dice = random.nextInt(6) + 1;
if (dice == 1) {
money += 100;
System.out.println("소지금이 증가했습니다.");
} else if (dice == 2) {
money /= 2;
System.out.println("소지금이 절반이 되었습니다.");
} else if (dice == 6) {
String f = getFruit();
System.out.println("과일(" + f + ")을 받았습니다.");
fruits.add(f);
} else {
System.out.println("변한 것이 없습니다.");
}
}
public Memento createMemento() {
Memento m = new Memento(money);
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
String f = (String) it.next();
if (f.startsWith("맛있는")) {
m.addFruit(f);
}
}
return m;
}
public void restoreMemento(Memento memento) { // undo 를 실행한다
this.money = memento.money;
this.fruits = memento.fruits;
}
public String toString() {
return "[money = " + money + ", fruits = " + fruits + "]";
}
private String getFruit(){
String prefix = "";
if (random.nextBoolean()) {
prefix = "맛있는";
}
return prefix + fruitsname[random.nextInt(fruitsname.length)];
}
}
class Memento {
int money;
ArrayList<String> fruits;
Memento(int money){
this.money = money;
this.fruits = new ArrayList<String>();
}
void addFruit(String fruit){
fruits.add(fruit);
}
public int getMoney() {
return money;
}
}
public class MementoTest {
public static void main(String[] args) {
Gamer gamer = new Gamer(100);
Memento memento = gamer.createMemento();
ArrayList<Memento> history = new ArrayList<Memento>();
for (int i = 0; i < 100; i++) {
System.out.println("==== " + i);
System.out.println("현상: " + gamer);
gamer.bet();
System.out.println("소지금은 " + gamer.getMoney() + "원이 되었습니다.");
if (gamer.getMoney() > memento.getMoney() ) {
System.out.println(" (많이 증가했으므로 현재의 상태를 저장하고 다시 게임을 시작합니다.)");
memento = gamer.createMemento();
history.add(memento);
} else if (gamer.getMoney() < memento.getMoney() / 2) {
System.out.println(" (많이 감소했으므로 이전의 상태로 복원하고 다시 게임을 시작합니다.)");
gamer.restoreMemento(memento);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("");
}
}
}
LIST
'4차산업혁명의 일꾼 > Java&Spring웹개발과 서버 컴퓨터' 카테고리의 다른 글
구조안을 돌아다니며 처리하는 패턴(Iterator, Vsitor, Chain of responsibility) (0) | 2023.03.06 |
---|---|
복잡한 연결을 단순하고 간단하게 처리하는 패턴(Facade, Mediator) (0) | 2023.03.06 |
디자인패턴 5 : Adapter Pattern(호환) (0) | 2023.03.04 |
디자인패턴 4 동일시하기-equate : (Decorator)와 위임하기(Composite) (0) | 2023.03.03 |
[OKKY 2월 세미나] AI사용으로 개발 속도 2배 높이기 정리 (0) | 2023.02.27 |