JUINTINATION
옵서버(Observer) 패턴 본문
반응형
옵서버 패턴이란?
통보 대상 객체의 관리를 Subject 클래스와 Observer 인터페이스로 일반화하여 데이터의 변경이 발생하였을 때 상대 클래스 및 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 데이터 변경을 통보하는 클래스(ConcreteSubject)의 통보 대상 클래스/객체(ConcreteObserver)에 대한 의존성을 제거하여 통보 대상 클래스나 대상 객체의 변경에도 ConcreteSubject 클래스를 수정 없이 사용하는 패턴이다.
다음과 같이 여러 가지 방식으로 성적을 출력하는 프로그램이 있다고 하자.
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
ScoreRecord scoreRecord = new ScoreRecord();
DataSheetView dataSheetView = new DataSheetView(scoreRecord, 3);
scoreRecord.setDataSheetView(dataSheetView);
for (int index = 1; index <= 5; index++) {
int score = index * 10;
System.out.println("Adding " + score);
scoreRecord.addScore(score);
}
}
}
class ScoreRecord {
private List<Integer> scores = new ArrayList<Integer>();
private DataSheetView dataSheetView;
public void setDataSheetView(DataSheetView dataSheetView) {
this.dataSheetView = dataSheetView;
}
public void addScore(int score) {
scores.add(score);
dataSheetView.update();
}
public List<Integer> getScoreRecord() {
return scores;
}
}
class DataSheetView {
private ScoreRecord scoreRecord;
private int viewCount;
public DataSheetView(ScoreRecord scoreRecord, int viewCount) {
this.scoreRecord = scoreRecord;
this.viewCount = viewCount;
}
public void update() {
List<Integer> record = scoreRecord.getScoreRecord();
displayScores(record, viewCount);
}
private void displayScores(List<Integer> record, int viewCount) {
System.out.print("List of " + viewCount + " entries: ");
for (int i = 0; i < viewCount && i < record.size(); i++) {
System.out.print(record.get(i) + " ");
}
System.out.println();
}
}
- 위 코드의 문제점
- 성적을 다른 방식으로 출력하고 싶다면 기존의 코드를 수정해야 한다.
- 유지 보수의 어려움
- OCP 위반
- 성적을 다른 방식으로 출력하고 싶다면 기존의 코드를 수정해야 한다.
예를 들어 성적의 최소/최대 값만 출력하기 위해선 코드를 다음과 같이 수정해야 한다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Client {
public static void main(String[] args) {
ScoreRecord scoreRecord = new ScoreRecord();
MinMaxView minMaxView = new MinMaxView(scoreRecord);
scoreRecord.setDataSheetView(minMaxView);
for (int index = 1; index <= 5; index++) {
int score = index * 10;
System.out.println("Adding " + score);
scoreRecord.addScore(score);
}
}
}
class ScoreRecord {
private List<Integer> scores = new ArrayList<Integer>();
private MinMaxView minMaxView;
public void setDataSheetView(MinMaxView minMaxView) {
this.minMaxView = minMaxView;
}
public void addScore(int score) {
scores.add(score);
minMaxView.update();
}
public List<Integer> getScoreRecord() {
return scores;
}
}
class MinMaxView {
private ScoreRecord scoreRecord;
public MinMaxView(ScoreRecord scoreRecord) {
this.scoreRecord = scoreRecord;
}
public void update() {
List<Integer> record = scoreRecord.getScoreRecord();
displayMinMax(record);
}
private void displayMinMax(List<Integer> record) {
int min = Collections.min(record, null);
int max = Collections.max(record, null);
System.out.println("Min: " + min + " Max: " + max);
}
}
기능을 변경하거나 새로운 기능을 추가할 때마다 ScoreRecord 클래스를 수정해야 하므로 OCP를 위반한다.
이를 해결하기 위해 다음과 같이 클래스 다이어그램을 만들 수 있다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Client {
public static void main(String[] args) {
ScoreRecord scoreRecord = new ScoreRecord();
DataSheetView dataSheetView3 = new DataSheetView(scoreRecord, 3);
DataSheetView dataSheetView5 = new DataSheetView(scoreRecord, 5);
MinMaxView minMaxView = new MinMaxView(scoreRecord);
scoreRecord.attach(dataSheetView3);
scoreRecord.attach(dataSheetView5);
scoreRecord.attach(minMaxView);
for (int index = 1; index <= 5; index++) {
int score = index * 10;
System.out.println("Adding " + score);
scoreRecord.addScore(score);
}
}
}
interface Observer {
void update();
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
class ScoreRecord extends Subject {
private List<Integer> scores = new ArrayList<Integer>();
public void addScore(int score) {
scores.add(score);
notifyObservers();
}
public List<Integer> getScoreRecord() {
return scores;
}
}
class DataSheetView implements Observer {
private ScoreRecord scoreRecord;
private int viewCount;
public DataSheetView(ScoreRecord scoreRecord, int viewCount) {
this.scoreRecord = scoreRecord ;
this.viewCount = viewCount ;
}
@Override
public void update() {
List<Integer> record = scoreRecord.getScoreRecord() ;
displayScores(record, viewCount);
}
private void displayScores(List<Integer> record, int viewCount) {
System.out.print("List of " + viewCount + " entries: ");
for (int i = 0 ; i < viewCount && i < record.size(); i++) {
System.out.print(record.get(i) + " ");
}
System.out.println();
}
}
class MinMaxView implements Observer {
private ScoreRecord scoreRecord;
public MinMaxView(ScoreRecord scoreRecord) {
this.scoreRecord = scoreRecord;
}
@Override
public void update() {
List<Integer> record = scoreRecord.getScoreRecord();
displayMinMax(record);
}
private void displayMinMax(List<Integer> record) {
int min = Collections.min(record, null);
int max = Collections.max(record, null);
System.out.println("Min: " + min + " Max: " + max);
}
}
옵서버 패턴 컬레보레이션
Subject : ConcreteObserver 객체를 관리하는 클래스
Observer : 데이터의 변경을 통보받는 인터페이스
ConcreteObserver : ConcreteSubject의 변경을 통보받는 Observer 하위 클래스
ConcreteSubject : 변경 관리 대상이 되는 데이터가 있는 클래스
옵서버 패턴을 이용한 간단한 예제
- 방범 센서는 침입자를 감지한다.
- 관리자가 방범 센서의 경보 수준(1~3)을 설정할 수 있다.
- 침입자가 감지되었다면, 방 안의 불을 켠다. (경보수준 1, 2, 3에서만 동작)
- 침입자가 감지되었다면, 알람을 작동한다. (경보수준 2, 3에서만 동작)
- 침입자가 감지되었다면, 경찰에게 연락한다. (경보수준 3에서만 동작)
클래스 다이어그램
소스코드
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
SensorSubject sensorSubject = new SensorSubject();
sensorSubject.setSecurityLevel(2);
LightObserver lightObserver = new LightObserver(sensorSubject, 1);
AlarmObserver alarmObserver = new AlarmObserver(sensorSubject, 2);
CallPoliceObserver callPoliceObserver = new CallPoliceObserver(sensorSubject, 3);
sensorSubject.attach(lightObserver);
sensorSubject.attach(alarmObserver);
sensorSubject.attach(callPoliceObserver);
boolean[] isInvadeds = new boolean[] {false, false, true, false, true};
for (boolean isInvaded : isInvadeds) {
System.out.println(isInvaded ? "침입 발생" : "평화로운 상태");
sensorSubject.setIsInvaded(isInvaded);
}
}
}
class SensorSubject extends Subject {
private int securityLevel = 1;
private boolean isInvaded = false;
public void setSecurityLevel(int securityLevel) {
this.securityLevel = securityLevel;
}
public int getSecurityLevel() {
return securityLevel;
}
public void setIsInvaded(boolean isInvaded) {
this.isInvaded = isInvaded;
notifyObservers();
}
public boolean getIsInvaded() {
return isInvaded;
}
}
class LightObserver implements Observer {
private SensorSubject sensorSubject;
private int securityLevel;
public LightObserver(SensorSubject sensorSubject, int securityLevel) {
this.sensorSubject = sensorSubject;
this.securityLevel = securityLevel;
}
@Override
public void update() {
if (sensorSubject.getIsInvaded()) {
alert();
}
}
private void alert() {
if (sensorSubject.getSecurityLevel() >= 1) {
System.out.println("방 안의 불을 켠다.");
}
}
}
class AlarmObserver implements Observer {
private SensorSubject sensorSubject;
private int securityLevel;
public AlarmObserver(SensorSubject sensorSubject, int securityLevel) {
this.sensorSubject = sensorSubject;
this.securityLevel = securityLevel;
}
@Override
public void update() {
if (sensorSubject.getIsInvaded()) {
alert();
}
}
private void alert() {
if (sensorSubject.getSecurityLevel() >= securityLevel) {
System.out.println("알람을 작동한다.");
}
}
}
class CallPoliceObserver implements Observer {
private SensorSubject sensorSubject;
private int securityLevel;
public CallPoliceObserver(SensorSubject sensorSubject, int securityLevel) {
this.sensorSubject = sensorSubject;
this.securityLevel = securityLevel;
}
@Override
public void update() {
if (sensorSubject.getIsInvaded()) {
alert();
}
}
private void alert() {
if (sensorSubject.getSecurityLevel() >= securityLevel) {
System.out.println("경찰에게 연락한다.");
}
}
}
728x90
'JAVA객체지향디자인패턴' 카테고리의 다른 글
템플릿 메서드(Template Method) 패턴 (0) | 2023.07.16 |
---|---|
데커레이터(Decorator) 패턴 (0) | 2023.07.09 |
커맨드(Command) 패턴 (0) | 2023.07.06 |
스테이트(State) 패턴 (0) | 2023.07.06 |
스트래티지(Strategy) 패턴 (0) | 2023.07.06 |
Comments