옵저버 패턴 (Observer Pattern)
옵저버 패턴 (Observer Pattern)이란?
옵저버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다.
📌출처: https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4
옵저버 패턴은 일종의 푸쉬 서비스를 구현한다.
예를 들어 설명하자면 뉴스레터와 구독자가 합쳐진 것이 옵저버 패턴이다. (발행자는 subject / 구독자는 observer)
요소 | 설명 |
이름 | 옵저버(Observer) |
문제 | 1:n 관계에서의 정보 갱신 |
해결방안 | 사용자를 등록하고, 정보가 변동하는 경우 알려주고 값을 자동으로 갱신한다. |
결과 | 느슨한 커플링(loose coupling), 확장형 |
느슨한 결합이란?
- 두 객체가 느슨하게 결합되어 있다는 것은, 그 둘이 상호작용을 하긴 하지만 서로에 대해서 잘 모른다는 것을 의미한다.
- 옵저버 패턴에서는 서브젝트와 옵저버간 느슨한 결합이 만들어진다.
- 서브젝트가 옵저버에 대해서 아는 것은 특정 인터페이스를 구현한다는 것뿐이다.
- 새로운 옵저버는 쉽게 추가하거나 제거 가능(실행 중에도 가능)
- 옵저버가 새로 생겨도 subject는 바뀌지 않는다.
- 서브젝트와 옵저버는 독립적으로 재사용이 가능하다.
package oop_3;
import javax.swing.*;
import java.awt.event.WindowListener;
public abstract class FrameWindow {
private JFrame frame;
public FrameWindow(String title, int x, int y, int width, int height) {
frame = createWindow(title, x, y, width, height);
}
public FrameWindow(String title, int x, int y, int width, int height, WindowListener lis) {
frame = createWindow(title, x, y, width, height);
frame.addWindowListener(lis);
}
public JFrame createWindow(String title, int x, int y, int width, int height) {
JFrame frame;
frame = new JFrame(title);
frame.setBounds(x, y, width, height);
JPanel panel = createPanel(width, height);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
return frame;
}
public void closeWindow() {
frame.setVisible(false);
frame.dispose();
}
public void addWindowListener(WindowListener lis) {
frame.addWindowListener(lis);
}
public abstract JPanel createPanel(int width, int height);
}
package oop_3;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MainWindow extends FrameWindow implements ActionListener {
private static final String MAIN_TITLE = "Main Window";
private static final String TEXTFIELD_WINDOW_TITLE = "TextField Window";
private static final String LABEL_WINDOW_TITLE = "Label Window";
private static final String REMOVE_TEXTFIELD_OBSERVER_BUTTON_TITLE = "Remove TextField Window Observer";
private static final String REMOVE_LABEL_OBSERVER_BUTTON_TITLE = "Remove Label Window Observer";
private static final String ADD_TEXTFIELD_OBSERVER_BUTTON_TITLE = "Add TextField Window Observer";
private static final String ADD_LABEL_OBSERVER_BUTTON_TITLE = "Add Label Window Observer";
private static final String STOP_THREAD_BUTTON_TITLE = "Stop Generating Prime Number";
private static final int X = 250;
private static final int Y = 100;
private static final int WIDTH = 600;
private static final int HEIGHT = 200;
private static final int GAP = 50;
private boolean labelObserverAdded = true;
private boolean textFieldObserverAdded = true;
private JButton stopButton;
private JButton updateTextFieldObserverButton;
private JButton updateLabelObserverButton;
private PrimeObservableThread primeThread;
private TextFieldWindow textFieldWindow;
private LabelWindow labelWindow;
public MainWindow(String title) {
super(title, X, Y, WIDTH, HEIGHT);
textFieldWindow = new TextFieldWindow(TEXTFIELD_WINDOW_TITLE, X, Y + HEIGHT + GAP, WIDTH, HEIGHT);
labelWindow = new LabelWindow(LABEL_WINDOW_TITLE, X, Y + (HEIGHT + GAP) * 2, WIDTH, HEIGHT);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
primeThread.stopRunning();
textFieldWindow.closeWindow();
labelWindow.closeWindow();
System.exit(0);
}
});
primeThread = new PrimeObservableThread(); // 객체 생성
primeThread.addObserver(textFieldWindow);
primeThread.addObserver(labelWindow);
primeThread.run(); // 소수 생성 시작. 이 함수가 실행된 후에는 stopButton이 눌리기 전까지 무한 반복됨
}
public JPanel createPanel(int width, int height) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setPreferredSize(new Dimension(width, height));
updateTextFieldObserverButton = createButton(REMOVE_TEXTFIELD_OBSERVER_BUTTON_TITLE, this, width, height);
panel.add(updateTextFieldObserverButton);
updateLabelObserverButton = createButton(REMOVE_LABEL_OBSERVER_BUTTON_TITLE, this, width, height);
panel.add(updateLabelObserverButton);
stopButton = createButton(STOP_THREAD_BUTTON_TITLE, this, width, height);
panel.add(stopButton);
return panel;
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == updateTextFieldObserverButton) {
if (textFieldObserverAdded) {
primeThread.removeObserver(textFieldWindow);
updateTextFieldObserverButton.setText(ADD_TEXTFIELD_OBSERVER_BUTTON_TITLE);
textFieldObserverAdded = false;
}
else {
primeThread.addObserver(textFieldWindow);
updateTextFieldObserverButton.setText(REMOVE_TEXTFIELD_OBSERVER_BUTTON_TITLE);
textFieldObserverAdded = true;
}
}
else if (e.getSource() == updateLabelObserverButton) {
if (labelObserverAdded) {
primeThread.removeObserver(labelWindow);
updateLabelObserverButton.setText(ADD_LABEL_OBSERVER_BUTTON_TITLE);
labelObserverAdded = false;
}
else {
primeThread.addObserver(labelWindow);
updateLabelObserverButton.setText(REMOVE_LABEL_OBSERVER_BUTTON_TITLE);
labelObserverAdded = true;
}
}
else if (e.getSource() == stopButton) {
primeThread.stopRunning();
}
}
private JButton createButton(String text, ActionListener listener, int width, int height) {
JButton button = new JButton(text);
button.addActionListener(listener);
Dimension buttonDimension = new Dimension(width, height / 3);
button.setMaximumSize(buttonDimension);
button.setMinimumSize(buttonDimension);
button.setPreferredSize(buttonDimension);
return button;
}
public static void main(String[] args) {
MainWindow mainWindow = new MainWindow(MainWindow.MAIN_TITLE);
}
}
package oop_3;
import java.util.ArrayList;
public class PrimeObservableThread implements Runnable, Observable {
private static final int SLEEPTIME = 500;
private int primeNumber;
private int numCount;
private boolean first = true;
private boolean stopRunning = false;
ArrayList<Observer> observers;
public PrimeObservableThread() {
observers = new ArrayList<Observer>();
}
public void addObserver(Observer o) {
// if (observers.contains(o)) {
// System.out.println("Observers already contained the observer.")
// }
if (!observers.contains(o)) {
observers.add(o);
}
}
public void removeObserver(Observer o) {
if (observers.contains(o)) {
observers.remove(o);
}
}
public void notifyObservers() {
for (Observer o : observers) {
o.updateText("" + primeNumber);
}
}
public int getPrimeNumber() {
return primeNumber;
}
public void stopRunning() {
stopRunning = true;
}
public void startRunning() {
stopRunning = false;
run();
}
private void generatePrimeNumber() {
while (stopRunning == false) {
if (first) {
first = false;
primeNumber = 2; // 泥� 踰덉㎏ �냼�닔�뒗 2
System.out.println(primeNumber);
numCount = 1; // �떎�쓬 �떒怨꾨��꽣�뒗 2瑜� �뜑�빐�꽌 ���닔留� �솗�씤�븯誘�濡� 1濡� 諛붽퓭�꽌 �떎�쓬 �닽�옄瑜� 3�쑝濡� 留뚮뱾�뼱�빞 �븿
} else {
numCount += 2; // 2瑜� �젣�쇅�븳 吏앹닔�뒗 �냼�닔媛� �맆 �닔 �뾾�쓬. �뵲�씪�꽌 ���닔留� 寃��궗
if (isPrimeNumber(numCount)) {
primeNumber = numCount;
notifyObservers();
System.out.println(primeNumber);
}
}
try {
Thread.sleep(SLEEPTIME); // 1珥� �돹
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private boolean isPrimeNumber(int n) {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
@Override
public void run() {
generatePrimeNumber();
}
}
package oop_3;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TextFieldWindow extends FrameWindow implements Observer {
private JTextField textField;
public TextFieldWindow(String title, int x, int y, int width, int height) {
super(title, x, y, width, height);
}
public void updateText(String msg) {
textField.setText(msg);
textField.validate();
}
public JPanel createPanel(int width, int height) {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
textField = new JTextField();
panel.add(textField);
panel.setPreferredSize(new Dimension(width, height));
return panel;
}
}
package oop_3;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LabelWindow extends FrameWindow implements Observer {
private JLabel label;
public LabelWindow(String title, int x, int y, int width, int height) {
super(title, x, y, width, height);
}
public void updateText(String msg) {
label.setText(msg);
label.validate();
}
public JPanel createPanel(int width, int height) {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
label = new JLabel();
panel.add(label);
panel.setPreferredSize(new Dimension(width, height));
return panel;
}
}
package oop_3;
public interface Observer {
void updateText(String s);
}
package oop_3;
public interface Observable {
void addObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
'Java > 고급객체지향' 카테고리의 다른 글
고급객체지향 프로그래밍 - 데코레이터 패턴 (Decorator Pattern) (0) | 2021.10.19 |
---|---|
고급객체지향 프로그래밍 - 스윙 (Swing) (0) | 2021.10.18 |
고급객체지향 프로그래밍 - 전략 패턴strategy pattern) (0) | 2021.10.18 |
고급객체지향 프로그래밍 - 제네릭스(Generics) (0) | 2021.10.06 |
#1. 과제 (0) | 2021.09.17 |