no_mkd
일.옵저버 패턴이란?
현실 세계의 신문과 구독자의 관계를 추상화한 것이 옵저버 패턴입니다. 한 종류의 신문이 여러 구독자에 대응하며, 구독자는 언제든지 구독을 해제할 수 있고, 미구독 독자도 언제든지 구독을 시작할 수 있습니다. 새로운 신문이 발행되면 모든 구독자가 새로운 내용을 받습니다.
옵저버 패턴에서는 신문을 주제 Subject 라고 하고, 구독자를 관찰자 Observer 라고 합니다. 하나의 Subject 가 여러 Observer 에 의해 관찰되며, Observer 는 언제든지 관찰을 해제할 수 있고, 새로운 Observer 도 언제든지 Subject 를 관찰할 수 있습니다. Subject 의 내용이 변경되면 모든 Observer 에게 통지됩니다.
이.예를 들자면
많은 온라인 게임에는 퀴즈 이벤트가 있으며, 퀴즈 이벤트에 참여하는 모든 플레이어가 동시에 문제 정보를 받습니다 (지연은 무시). 이벤트에 참여하지 않은 플레이어는 중간에 참여할 수 있고, 답변 중인 플레이어도 언제든지退出할 수 있습니다. 이 예에서 게임 서버가 '일'이고, 플레이어가 '다'이며, 문제 정보가 그들 사이에서 전달되는 메시지입니다. 이상의 요구사항을 만족하는 클래스를 어떻게 설계할 수 있을까요? 옵저버 패턴을 시도해 봅시다.
먼저, Subject 추상 클래스를 정의합니다:
package ObserverPattern;import java.util.ArrayList;
/**
- @author ayqy
- 定义 Subject 抽象类
*/ public abstract class Subject { ArrayList<Observer> observers = new ArrayList<Observer>();//观察者列表
/**
* 注册主题
* @param o 申请注册该主题的 Observer
*/
public void registSubject(Observer o)
{
observers.add(o);
}
/**
* 删除主题
* @param o
*/
public void reomveSubject(Observer o)
{
int index = observers.indexOf(o);
observers.remove(index);
}
/**
* 通知所有观察者
* @param arg 该 Subject 想要传递给 Observers 的数据
*/
public void notifyObservers(Object arg)
{
for(Observer o : observers)
o.update(this, arg);
}
}
주의하세요. 여기서는 인터페이스가 아닌 추상 클래스를 사용했습니다. 왜일까요? Subject 의 행동은 fixed 이며, 서브클래스에 의해 확장될 필요가 없기 때문입니다. 유사하게, Observer 추상 클래스를 정의합니다:
package ObserverPattern;/**
- @author ayqy
- 定义 Observer 抽象类
*/ public abstract class Observer { Subject subject = null;//定义该 Observer 所关注的 Subject
public abstract void update(Subject subject, Object arg);//定义 Observer 的更新接口
}
이제 커스텀 Subject 인 GameServer 를 구현합니다:
package ObserverPattern;/**
- @author ayqy
- 定义 GameServer 类,继承自 Subject 基类,负责发布题目
*/ public class GameServer extends Subject{ Question ques;//定义题目
public Question getQues() {
return ques;
}
public void setQues(Question q) {
this.ques = q;
super.notifyObservers(ques);//调用父类方法通知所有 Observer
}
}
P.S. GameServer 클래스의 멤버 변수 Question 은 문제 정보의 간단한 캡슐화이며, Question 클래스에는 문제 번호 no 와 문제 내용 content 의 두 가지 정의와, 문제 설명 정보를 반환하는 toString 메서드가 포함되어 있습니다. 이제 커스텀 Observer 인 Player 를 구현합니다:
package ObserverPattern;/**
- @author ayqy
- 定义 PlayerA,继承自 Observer 基类,负责接收新题目
*/ public class PlayerA extends Observer{
public PlayerA(Subject sub)
{
subject = sub;
}
@Override
public void update(Subject subject, Object arg) {
Question q = (Question)arg;
System.out.println("PlayerA received " + q.toString());
}
}
P.S. 클래스 계층을 더 명확하게 하기 위해, 여기서는 Player 베이스 클래스를 정의하지 않았습니다. PlayerB 와 PlayerC 를 복사하여 쉽게 만들 수 있습니다. 자세한 내용은 생략합니다.
이제 모의 퀴즈 이벤트 준비가 종료되었습니다. 다음으로, 테스트 클래스를 정의하여 옵저버 패턴의 매력을 보여줍니다.
삼.효과 예시
다음과 같은 Test 클래스를 정의합니다:
package ObserverPattern;/**
- @author ayqy
- 实现一个测试类,模拟网络游戏答题活动(游戏服务器按时更新题目信息并通知所有参与答题的玩家)
*/ public class Test { public static void main(String[] args) { System.out.println("答题活动即将开始。。"); //创建服务器 GameServer gs = new GameServer(); //创建玩家 ABC Observer playerA = new PlayerA(gs); Observer playerB = new PlayerB(gs); Observer playerC = new PlayerC(gs); //为 AB 注册 Subject,C 对答题不感兴趣,拒绝注册 gs.registSubject(playerA); gs.registSubject(playerB); System.out.println("玩家 AB 成功参与答题活动。。"); System.out.println("答题活动正式开始。。"); gs.setQues(new Question(1, "第一题")); gs.setQues(new Question(2, "第二题")); System.out.println("玩家 A 不想玩了,退出答题活动。。"); gs.reomveSubject(playerA); gs.setQues(new Question(3, "第三题")); System.out.println("玩家 C 想中途加入活动"); gs.registSubject(playerC); gs.setQues(new Question(4, "第四题")); System.out.println("答题活动结束。。"); } }
실행 결과 예시:

사.요약
위의 예에서 옵저버 패턴의 특징을 알 수 있습니다:
- 옵저버 패턴을 이용하면 객체 간의 '일대다' 의존 관계를 쉽게 설정할 수 있습니다.
- 옵저버 패턴의 메커니즘을 이용하면 이 의존 관계의 동적 유지를 쉽게 실현할 수 있습니다.
아직 댓글이 없습니다