何时使用观察者模式
1、触发联动:当修改目标状态时就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法。
2、建议在下面三种情况下使用观察者模式:
1.当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化时
2.如果在封盖一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有对少对象需要被连带改变
3.当一个对象必须通知其他的对象,但是你又希望这个对象和其他被他通知的对象是松散耦合的
实现方式一:观察者模式 经典模版
观察者模式的结构:Subject(被观察者或者说是目标类):要有添加观察者,删除观察者,和提醒观察者(当被观察者的状态发生改变的时候调用这个方法)的方法,Observe(观察者):要有更新方法(当观察者状态发生改变调用提醒方法后观察者通过更新方法来做出不同响应(动作))。
ConcreteSubject是Subject接口的实现类
ConcreteObserver是Observer接口的实现类
目标(被观察者):
/**
* 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口
*/
public class Subject {
// 用来保存注册的观察者对象
private List<Observer> observers = new ArrayList<Observer>();
// attach detach notifyObservers
// 把订阅天气的人添加到订阅者列表中
public void attach(Observer observer) {
observers.add(observer);
}
/**
* 删除集合中的指定观察者
* @param observer
*/
public void detach(Observer observer) {
observers.remove(observer);
}
/**
* 通知所有注册的观察者对象
*/
protected void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
}
目标(被观察者)具体实现:
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
*/
public class ConcreteSubject extends Subject {
// 目标对象的状态
private String Content;
public String getSubjectState() {
return Content;
}
public void setSubjectState(String Content) {
this.Content = Content;
// 内容有了,通知所有的订阅的人
this.notifyObservers();
}
}
观察者接口:
/**
* 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
*/
public interface Observer {
/**
* 更新的接口 传入目标对象,方便获取相应的目标对象的状态
* @param subject
*/
void update(Subject subject);
}
观察者接口的具体实现:
/**
* 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
*/
public class ConcreteObserver implements Observer {
// 观察者的名字,是谁收到了这个讯息
private String observerName;
// 观察者的状态,这个消息从目标处获取
private String observerState;
// 提醒的内容
private String remindTing;
/**
* 获取目标类的状态同步到观察者的状态中
*/
@Override
public void update(Subject subject) {
observerState = ((ConcreteSubject) subject).getSubjectState();
System.out.println(observerName + "收到了, " + observerState + " , " + remindTing);
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
public String getRemindTing() {
return remindTing;
}
public void setRemindTing(String remindTing) {
this.remindTing = remindTing;
}
}
测试类:
public static void main(String[] args) {
// 1.创建目标
ConcreteSubject weather = new ConcreteSubject();
// 2.创建观察者
ConcreteObserver observerGirl = new ConcreteObserver();
observerGirl.setObserverName("小明的女朋友");
observerGirl.setRemindTing("是我们的第一次约会,地点街心公园,不见不散哦");
ConcreteObserver observerMum = new ConcreteObserver();
observerMum.setObserverName("老妈");
observerMum.setRemindTing("是一个购物的好日子,明天去天虹扫货");
// 3.注册观察者
weather.attach(observerGirl);
weather.attach(observerMum);
// 4.目标发布天气
weather.setSubjectState("#明天天气晴朗,蓝天白云,气温28度#");
}
实现方式二:利用Java提供的观察者实现 观察者模式
Java 实现 VS 自己实现的对比四点:
(1)不需要再定义观察者和目标接口(JDK已经定义)。
(2)具体的目标实现里面不需要再维护观察者的注册信息,Java中的Observable类里面已经实现。
(3)触发通知的方式有一点变化,要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能。
(4)具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个Java在定义的时候,已经考虑。
目标(被观察者):
/**
* 被观察者的具体实现
*/
public class ConcreteSubject extends Observable {
// 变化的具体内容
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
// 发生变化,就要通知所有的观察者
// 注意在通知之前,在用Java中的Observer模式的时候,下面这句话不可少
this.setChanged();
// 然后主动通知,这里我们先用推的方式
this.notifyObservers(content);
// 如果是拉的方式,我们就调用
// this.notifyObservers();
}
}
观察者:
/**
* 具体的观察者对象
*/
public class ConcreteObserver implements Observer{
//观察者名称的变量
private String observerName;
@Override
public void update(Observable o, Object arg) {
//第一种是推的方式
System.out.println(observerName+"收到了消息,目标推送过来的是"+arg);
//第二种是拉的方式
System.out.println(observerName+"收到了消息,主动到目标对象中去拉,拉的内容是"+((ConcreteSubject)o).getContent());
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
}
测试类:
public static void main(String[] args) {
// 创建一个目标,也就是被观察者
ConcreteSubject subject = new ConcreteSubject();
// 创建小明的女朋友作为观察者
ConcreteObserver girl = new ConcreteObserver();
girl.setObserverName("小明的女朋友");
// 创建小明的老妈作为观察者
ConcreteObserver mum = new ConcreteObserver();
mum.setObserverName("小明的老妈");
// 注册观察者
subject.addObserver(girl);
subject.addObserver(mum);
// 目标更新天气情况了
subject.setContent("天气晴,气温28度");
}
观察者优缺点
1、观察者模式的优点:
(1)观察者模式实现了观察者和目标之间的抽象耦合
(2)观察者模式实现了动态联动
(3)观察者模式支持广播通信
2、观察者模式的缺点:可能会引起无谓的操作。
实现方式三:区别对待观察者场景问题 (灵活定制观察者)
区别观察者模式是,目标父类不实现通知方法,在子类中实现有区别的通知方法。
区别对待的观察者模型中和通用观察者模型的区别在于:要根据不同的观察者来进行不同的推送,所以区别在于目标类中的通知更新方法需要在具体的目标类中进行实现。(因为需要根据不同的情况进行更新,所以需要在具体的目标类中实现刚刚那个方法)
区别对待观察者,逻辑判断让观察者实现更符合逻辑,定义到接口观察者中,setRule(),让小明女朋友和老妈自己去定义
目标(被观察者)的抽象方法:
public abstract class Subject {
// 用来保存注册的观察者对象
public List<Observer> observers = new ArrayList<Observer>();
// attach detach abstract notifyObservers
// 把观察者添加到订阅者列表中
public void attach(Observer observer) {
observers.add(observer);
}
// 删除集合中指定的订阅天气的人
public void detach(Observer observer) {
observers.remove(observer);
}
protected abstract void notifyObservers();
}
目标(被观察者)的具体实现:
public class ConcreteSubject extends Subject {
// "晴天" "下雨" "下雪"
// 目标对象的状态
private String Content;
@Override
protected void notifyObservers() {
// 循环所有注册的观察者
for (Observer observer : observers) {
// 情况之一:
// 如果天气是晴天,按照小明的女朋友需要下雨的条件,小明的老妈需要下雨或下雪的条件,则她们俩就都不需要通知了。
// 情况之二:
// 如果天气是下雨,则小明的女朋友需要通知,而小明的老妈也需要通知。
// 情况之三:
// 如果天气是下雪,则只需要通知小明的老妈。
if("下雨".equals(this.getContent())){
if("小明的女朋友".equals(observer.getObserverName())){
observer.update(this);
}
if("小明的老妈".equals(observer.getObserverName())){
observer.update(this);
}
}
if("下雪".equals(this.getContent())){
if("小明的老妈".equals(observer.getObserverName())){
observer.update(this);
}
}
}
}
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
this.notifyObservers();
}
}
观察者的接口:
/**
* 定义一个更新的接口方法给那些在目标发生改变的时候被通知的观察者对象调用
*/
public interface Observer {
// 更新的接口
public void update(Subject subject);
// 设置观察者名称
public void setObserverName(String observerName);
// 取得观察者名称
public String getObserverName();
}
观察者的具体实现:
public class ConcreteObserver implements Observer {
// 观察者的名称
private String observerName;
// 天气情况的内容
private String content;
// 提醒的内容
private String remindThing;
@Override
public void update(Subject subject) {
content = ((ConcreteSubject) subject).getContent();
System.out.println(observerName + "收到了<" + content + ">," + remindThing);
}
@Override
public void setObserverName(String observerName) {
this.observerName = observerName;
}
@Override
public String getObserverName() {
return observerName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRemindThing() {
return remindThing;
}
public void setRemindThing(String remindThing) {
this.remindThing = remindThing;
}
}
测试类:
public static void main(String[] args) {
// 1.创建目标
ConcreteSubject weather = new ConcreteSubject();
// 2.创建观察者
ConcreteObserver observerGirl = new ConcreteObserver();
observerGirl.setObserverName("小明的女朋友");
observerGirl.setRemindThing("下雨了,安静的呆在家里吧");
ConcreteObserver observerMum = new ConcreteObserver();
observerMum.setObserverName("小明的老妈");
observerMum.setRemindThing("不管下雨还是下雪,我都不出门了");
// 3.注册观察者
weather.attach(observerGirl);
weather.attach(observerMum);
// 4.目标发布天气
weather.setContent("下雪");
}