[Source] PPT of sk of NEU
一、问题引入:照顾孩子
- 初始需求:当孩子哭闹时,父亲必须立即检查孩子,不允许孩子哭
针对初始需求的解决方法如下:
// Baby.java
public class Baby {
volatile String state="happy";
void cry(){
state="crying";
new Thread(){
@Override
public void run(){
while(state.equals("crying")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("I will cry until die!");
}
}
}.start();
}
}
// Father.java
public class Father {
Baby baby;
public void setBaby(Baby baby) {
this.baby = baby;
}
void observeBaby(){
new Thread(){
public void run(){
while(true){
if(baby.state.equals("crying")){
System.out.println("Do not cry!");
baby.state="happy";
}
}
}
}.start();
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Father f = new Father();
Baby b = new Baby();
f.setBaby(b);
f.observeBaby();
System.out.println("After 5 seconds the baby will cry!");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b.cry();
}
}
诚然,Father 的observeBaby()方法的循环会浪费 CPU 时间
- 一次变更-更多Adults:当孩子哭闹时,父母立即检查孩子,不允许孩子哭
针对一次变更,可以简单的添加Mother类即可解决
// Mother.java
package observer.baby.second;
public class Mother {
Baby baby;
public void setBaby(Baby baby) {
this.baby = baby;
}
void observeBaby(){
new Thread(){
public void run(){
while(true){
if(baby.state.equals("crying")){
System.out.println("Mother: Do not cry!");
baby.state="happy";
}
}
}
}.start();
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Father f = new Father();
Mother m = new Mother();
Baby b = new Baby();
f.setBaby(b);
m.setBaby(b);
f.observeBaby();
m.observeBaby();
System.out.println("After 5 seconds the baby will cry!");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b.cry();
}
}
与初始需求解决方法一样,浪费 CPU 时间
- 二次变更-更多Baby:当多个孩子哭闹时,父母立即检查孩子,不允许孩子哭,如下图所示
诚然,不能采用与初始需求一致的解决方法,毕竟孩子的父母不能改变!
那么,如何解决二次变更的需求且避免浪费 CPU 时间呢?答案是采用观察者模式。
二、观察者模式
-
Observer Pattern,Also Known As:Dependents, Publish-Subscribe, Model-View
-
Intent:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically(定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新).
-
Motivation:The need to maintain consistency between related objects without making classes tightly coupled(需要保持相关对象之间的一致性而不使类紧密耦合)
-
适用情况:
- When an abstraction has two aspects, one dependent on the other(当一个抽象有两个方面,一个方面依赖于另一个方面时). Encapsulating these aspects in separate objects lets you vary and reuse them independently(封装这些方面到单独的对象中,使得可以独立地更改和重用它们).
- When a change to one object requires changing others, and you don’t know how many objects need to be changed(当对一个对象的更改需要更改其他对象且你不知道需要更改多少个对象时).
- When an object should be able to notify other objects without making assumptions about who these objects are(当一个对象应该能够通知其他对象而不假设这些对象是谁时). In other words, you don’t want these objects tightly coupled (换句话说,你不希望这些对象紧密耦合).
-
类图
- Subject
- Keeps track of its observers
- Provides an interface for attaching and detaching Observer objects
- Observer
- Defines an interface for update notification
- Subject
-
优点
- Minimal coupling between the Subject and the Observer (最小化Subject 与 Observer 的耦合)
- Observers can be added without modifying the subject
- Subject does not need to know the concrete class of an observer,just that each observer implements the update interface
- Subject and observer can belong to different abstraction layers
- Support for event broadcasting (支持事件广播)
- Subject sends notification to all subscribed observers
- Minimal coupling between the Subject and the Observer (最小化Subject 与 Observer 的耦合)
-
缺点
- Observers have no knowledge of each other’s presence, they can be blind to the cost of changing the subject.
- The simple update protocol provides no details on what changed in the subject. The observers have to discorver what changed themselves, thus we need to deduce(推断) the changes.
-
MVC user interface framework & Oberver Pattern :MVC 用户界面框架与观察者模式
- Model = Subject
- View = Observer
- Controller is whatever object changes the state of the subject (控制器是改变Subject状态的任何对象)
-
推模式 与 拉模式
- 推模式:
- Assumes subjects know something about their observers’ needs
- Make observers less reusable,because Subject classes make assumptions about Observer classes that might not always be true.
- 拉模式:
- Emphasizes the subject’s ignorance of its observers
- May be inefficient, because Observer classes must ascertain what changed without help from the Subject
- subject 发送给 observers 的 关于改变的信息的多少
- 推模式:多
- 拉模式:少
- 推模式:
-
观察者模式应用的设计原则:DIP,CRP
三、Java 内置的观察者模式
- Observable,即 Subject
- Observer
- 注意:使用Java 内置的观察者模式时,注意只有在setChange()被调用后, notifyObservers()才会去调用update(),原因如下
四、使用 Java 内置的观察者模式解决二次变更需求
-
如何使用 Java 内置的观察者模式
- For an Object to become an observer
- implement the Observer interface
- addObserver() on Observable object
- For the Observable to send notifications
- call the setChanged() method to signify that the state has changed
- call one of two notifyObservers() methods: n notifyObservers() n notifyObservers(Object arg)
- For an Observer to receive notifications
- update(Observable o, Object arg)
- If you want to “push” data to the observers you can pass the data as a data object to the notifyObserver(arg) method. If not, then the Observer has to “pull” the data it wants from the Observable object passed to it.
- update(Observable o, Object arg)
- For an Object to become an observer
-
针对 Father (an Object to become an observer),实现 Observer 接口
import java.util.Observable;
import java.util.Observer;
public class Father implements Observer {
@Override
public void update(Observable arg0, Object arg1) {
// TODO Auto-generated method stub
Baby baby;
if(arg0 instanceof Baby){
baby=(Baby) arg0;
if(baby.getState().equals("crying")){ //pull
System.out.println("Father: Do not cry!"+baby.toString());
baby.setState("happy");
}
}
}
}
- 针对 Baby (the Observable(Subject) to send notifications),继承 Observable 类
import java.util.Observable;
public class Baby extends Observable {
private volatile String state="happy";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
void cry(){
state="crying";
new Thread(){
public void run(){
while(state.equals("crying")){
System.out.println("I will cry until die!");
}
}
}.start();
this.setChanged();
this.notifyObservers();
}
}
- 测试
public class Test {
public static void main(String[] args) {
Father f = new Father();
Baby b = new Baby();
Baby b2 = new Baby();
b.addObserver(f);
b2.addObserver(f);
b.cry();
b2.cry();
}
}
五、使用观察者模式解决二次变更需求
- Subject 接口
public interface Subject {
public void attach(Observer o);
public void dettach(Observer o);
public void notifyObservers();
}
- Baby:实现 Subject 接口
public class Baby implements Subject {
ArrayList<Observer> arrayList;
private volatile String state;
void cry(){
state="cry";
new Thread(){
@Override
public void run(){
while(state.equals("cry")){
System.out.println("I will cry until die!");
}
}
}.start();
notifyObservers();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Baby() {
super();
this.arrayList = new ArrayList<Observer>();
}
@Override
public void attach(Observer o) {
// TODO Auto-generated method stub
arrayList.add(o);
}
@Override
public void dettach(Observer o) {
// TODO Auto-generated method stub
arrayList.remove(o);
}
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(Observer observer : arrayList){
observer.update(this);
}
}
}
- Observer 接口
public interface Observer {
public void update(Subject s);
}
- Father:实现Observer接口
public class Father implements Observer {
@Override
public void update(Subject s) {
Baby baby = (Baby) s;
if(baby.getState().equals("cry")){
System.out.println("Don't cry");
baby.setState("happy");
}
}
}
- Test
public class Test {
public static void main(String[] args) {
Father f = new Father();
Baby b = new Baby();
Baby b2 = new Baby();
b.attach(f);
b2.attach(f);
b.cry();
b2.cry();
}
}