设计模式三十六计之观察者模式(Observer)
1. 设计意图
定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项。
观察者模式简而言之
你别来找我,给我你的联系方式,有事我会主动联系你
2.案例演示
以当前最火热的吃鸡游戏作为一个简单的案例来演示观察者模式,当玩家进入游戏时,会收到游戏服务器推送的提示消息,随着游戏的进行,如果某个玩家被Kill掉了,游戏服务器会把此消息推送给房间里的其他玩家。在本案例中,“游戏” 是一个抽象的被观察者,"吃鸡游戏" 是具体的被观察者;“游戏玩家” 是一个抽象的观察者(接口),而玩家A、玩家B等是具体的观察者。案例的UML关系如下图:
image3. 示例代码
3.1 抽象的被观察者类(Subject)
AbstractGame.java
package com.ramostear.pattern.observer;
import java.util.ArrayList;
/**
* @author ramostear
* @create-time 2019/1/5 0005-23:27
* @modify by :
* @info:[抽象的被观测者类]
* @since:
*/
public abstract class AbstractGame {
/**
* 定义一个存放观察者的容器
*/
public final ArrayList<Observer> obsList = new ArrayList<>();
/**
* 注册观察者
* @param obs 观察者
* @param <T>
*/
public <T> void attach(Observer obs){
if (obs == null){
throw new NullPointerException("Observer is null.");
}else{
this.attachObs(obs);
}
}
/**
* 注册观察者
* @param obs
*/
private void attachObs(Observer obs){
if (obs == null){
throw new NullPointerException("class is null");
}else {
synchronized (obsList){
if(!obsList.contains(obs)){
obsList.add(obs);
}
}
}
}
/**
* 注销观察者
* @param obs 观察者
* @param <T>
*/
public <T> void detach(Observer obs){
if(obs == null){
throw new NullPointerException("Observer is null");
}else {
this.detachObs(obs);
}
}
/**
* 注销观察者
* @param obs
*/
private void detachObs(Observer obs){
if(obs == null){
throw new NullPointerException("Class is null");
}else{
synchronized (obsList){
obsList.remove(obs);
}
}
}
/**
* 通知所有的观察者
* @param messages
*/
public abstract void notifyAllObs(String...messages);
/**
* 通知某个观察者
* @param obs
* @param messages
*/
public abstract void notifyObs(Observer obs,String...messages);
}
AbstractGame类中定义了添加、删除和通知观察者的方法,同时有一个List类型的容器,用于保存已注册的观察者,当需要通知观察者时,从容器中取出观察者信息。
说明:抽象的被观察者可以定义成一个抽象类或者接口,本案例中采用的是抽象类
3.2 抽象的观察者接口(Observer)
Observer.java
package com.ramostear.pattern.observer;
/**
* @author ramostear
* @create-time 2019/1/5 0005-23:26
* @modify by :
* @info:[观察者接口]
* @since:
*/
public interface Observer {
/**
* 更新状态
* @param messages
*/
void update(String... messages);
}
在该接口中定义了一个update() 方法,当被观察者发出通知时,此方法会被调用。
3.3 具体被观察者(ConcreteSubject)
ChikenGame继承了AbstractGame类,并对通知方法进行了具体的实现。
ChikenGame.java
package com.ramostear.pattern.observer;
/**
* @author ramostear
* @create-time 2019/1/5 0005-23:55
* @modify by :
* @info:[吃鸡游戏类]
* @since:
*/
public class ChickenGame extends AbstractGame {
private String roomName;
public ChickenGame(String roomName) {
this.roomName = roomName;
}
public String getRoomName() {
return roomName;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
@Override
public void notifyAllObs(String... messages) {
obsList.forEach(obs->{
this.notifyObs(obs,messages);
});
}
@Override
public void notifyObs(Observer obs, String... messages) {
if (obs == null){
throw new NullPointerException("Observer is null");
}else{
obs.update(messages);
}
}
}
3.4 具体观察者(ConcreteObserver)
Gamer类实现了Observer接口,并对Observer的update方法进行了具体的实现;这里为了演示,只是简单的对消息进行输出。
Gamer.java
package com.ramostear.pattern.observer;
/**
* @author ramostear
* @create-time 2019/1/6 0006-0:06
* @modify by :
* @info:[游戏玩家]
* @since:
*/
public class Gamer implements Observer{
private String name;
public Gamer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void update(String... messages) {
System.out.println("玩家:"+name);
for (String message:messages){
System.out.println("消息->"+message+"\n");
}
}
}
3.5 测试本次案例
创建一个吃鸡游戏叫“三国吃鸡演义” ,将刘、关、张三个玩家注册到吃鸡游戏中。游戏发布消息给三个玩家,刘、关、张同时收到游戏发出的消息,当关羽挂掉后,只有刘、张两个玩家收到消息;当张飞再挂掉后,只有刘备收到消息。为了演示观察者模式,最后我们让关羽满血复活,此时刘、关二人收到游戏发出的消息。
App.java
package com.ramostear.pattern.observer;
/**
* @author ramostear
* @create-time 2019/1/6 0006-0:08
* @modify by :
* @info:[测试类]
* @since:
*/
public class App {
public static void main(String[] args){
ChickenGame game = new ChickenGame("三国吃鸡演义");
Gamer gamerLiu = new Gamer("刘备");
Gamer gamerZhang = new Gamer("张飞");
Gamer gamerGuan = new Gamer("关羽");
game.attach(gamerLiu);
game.attach(gamerGuan);
game.attach(gamerZhang);
game.notifyAllObs("欢迎进入"+game.getRoomName());
game.notifyAllObs(new String[]{"刘关张桃园三结义,开始三国吃鸡演义..."});
game.detach(gamerGuan);
game.notifyAllObs("#关羽:\"我去!被98K爆了,快来扶我一下!\"");
game.notifyAllObs("#刘备:\"我去,这货肥得一批!\"");
game.detach(gamerZhang);
game.notifyAllObs("#张飞:\"我去,这比是挂!\"");
game.notifyAllObs("#刘备:\"我去!咋这么多人,我凉了!\"");
game.attach(gamerGuan);
game.notifyAllObs("关羽满血复活");
game.notifyAllObs("#刘备:\"苟住,苟住就能赢!\"");
}
}
测试结果:
image
4. 适用性
当满足以下情况中的一种时使用观察者模式
- 当抽象有两个Aspect时,一个依赖于另一个。 将这些Aspact封装在单独的对象中可让您独立地改变和重用它们.
- 当一个对象的更改需要更改其他对象时,你不知道到底需要更改多少个关联的对象
- 当不希望多个对象之前发生紧耦合时
5. 真实案例
原文链接:https://www.ramostear.com/post/details/observer-design-pattern.html
网友评论