观察者模式(Observer Pattern)
定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。
属于行为型模式
观察者模式有时也叫做发布订阅模式。
适用的场景:
1、用于在关联行为之间建立一套触发机制的场景;
2、一个对象必须通知其他的对象,而并不知道这些对象是谁;
3、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象,可以使用观察者模式创建一种链式触发机制;
优点:观察者和被观察者之间建立了一个抽象的耦合;观察者模式支持广播通信;
缺点:
1、如果观察者对象有很多的直接或者间接的观察者的话,将所有的观察者都通知到会花很多的时间;
2、如果观察者与被观察者之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
3、观察者之间有过多的细节依赖,提高时间消耗及程序的复杂度;
注意事项:
1、JAVA中已经有了对观察者模式的支持类
2、避免循环引用
3、如果顺序执行,某一个观察者发生错误会导致系统卡壳,一般采用异步方式
简单消息发送示例:
public class Message {
private String username;
private String messageContent;
public String getMessageContent() {
return messageContent;
}
public void setMessageContent(String messageContent) {
this.messageContent = messageContent;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
public class WechatPlateform extends Observable {
private String nameOfPlateform = "WECHAT";
private static WechatPlateform wechatPlateform = null;
private WechatPlateform() {
}
public static WechatPlateform getInstance() {
if (wechatPlateform == null) {
synchronized (WechatPlateform.class) {
if (wechatPlateform == null) {
wechatPlateform = new WechatPlateform();
}
}
}
return wechatPlateform;
}
public String getNameOfPlateform() {
return nameOfPlateform;
}
public void sendMessage(Message message) {
System.out.println(this.nameOfPlateform + "平台上," + message.getUsername() + "发送了一条消息");
setChanged();
notifyObservers(message);
}
}
public class WeChatUser implements Observer {
private String username;
public WeChatUser(String username) {
this.username = username;
}
@Override
public void update(Observable o, Object arg) {
WechatPlateform w = (WechatPlateform)o;
Message message = (Message)arg;
System.out.println(w.getNameOfPlateform() + ":" + this.username + "收到一条来" + message.getUsername() + "自的消息:");
System.out.println(message.getMessageContent());
}
}
public class WechatTest {
public static void main(String[] args) {
WeChatUser weChatUser = new WeChatUser("jack");
WeChatUser weChatUser1 = new WeChatUser("Leonardo DiCaprio");
Message message = new Message();
message.setUsername("rose/Kate Winslet");
message.setMessageContent("oh my god,jack,haven't seen you for a long time,I almost can't recognize you! the "
+ "size of your clothes is getting bigger than ever before!");
WechatPlateform wechatPlateform = WechatPlateform.getInstance();
wechatPlateform.addObserver(weChatUser1);
wechatPlateform.addObserver(weChatUser);
wechatPlateform.sendMessage(message);
}
}
运行结果:
WECHAT平台上,rose/Kate Winslet发送了一条消息
WECHAT:jack收到一条来rose/Kate Winslet自的消息:
oh my god,jack,haven't seen you for a long time,I almost can't recognize you! the size of your clothes is getting bigger than ever before!
WECHAT:Leonardo DiCaprio收到一条来rose/Kate Winslet自的消息:
oh my god,jack,haven't seen you for a long time,I almost can't recognize you! the size of your clothes is getting bigger than ever before!
结构关系图如下:
主要的创建步骤:
1、创建WechatPlateform 类,为被观察者,需要绑定观察者【通过继承 java.util.Observable类,其中的Vector<Observer> 属性集合就是用来存储观察者】,SendMessage()方法中调用了父类的notifyObservers()方法,用于通知观察者,并做出相应的操作。
2、创建WechatUser观察者,继承了Observer类,实现update(Observable obs,Object obj)用于对被观察者发生改变后做出对应的操作。
3、创建Message类,消息主体类,可以不加,此处只是方便用于展示。
4、创建客户端调用,就是测试类的main()方法中的内容。
键盘触发事件,示例:
public class Events {
//事件源,事件的触发者
private Object source;
//事件触发通知谁
private Object target;
//事件触发后需要做出什么反映,回调函数
private Method callback;
//触发的什么事件,时间的名称
private String trigger;
//事件触发的时间
private Date time;
public static Events getInstance(String trigger, Object source, Object object) {
Method[] methods = object.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals(trigger)) {
Events events = new Events();
events.setSource(source);
events.setCallback(method);
events.setTarget(object);
events.setTrigger(trigger);
events.setTime(new Date());
return events;
}
}
return null;
}
public void setSource(Object source) {
this.source = source;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Method getCallback() {
return callback;
}
public void setCallback(Method callback) {
this.callback = callback;
}
public void setTrigger(String trigger) {
this.trigger = trigger;
}
public void setTime(Date time) {
this.time = time;
}
@Override
public String toString() {
return "Events{\n " + "source=" + source + ",\n target=" + target + ", \n callback=" + callback + ",\n "
+ "trigger='" + trigger + '\'' + ",\n time=" + time + "\n}";
}
}
public class Listener {
Map<String, Events> objectMap = null;
public Listener() {
this.objectMap = new HashMap<>();
}
public void addListener(String trigger, Events object) {
objectMap.put(trigger, object);
}
public void trigger(String trigger) {
Events t = objectMap.get(trigger);
t.setSource(this);
try {
t.getCallback().invoke(t.getTarget(), t);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public enum KeybordEventEnum {
ENTER("enter", "回车"),
CAPSLOCK("capsLock", "大写锁定"),
ESC("esc", "退出"),
TAB("tab", "制表tab");
private String eventName;
private String description;
KeybordEventEnum(java.lang.String eventName, java.lang.String description) {
this.eventName = eventName;
this.description = description;
}
public String getEventName() {
return eventName;
}
}
public class KeybordCallback {
public void enter(Events events) {
System.out.println("=======callback点击回车按钮:\n" + events);
}
public void capsLock(Events events) {
System.out.println("=======callback点击大写锁定按钮:\n" + events);
}
public void esc(Events events) {
System.out.println("=======callback点击退出esc按钮:\n" + events);
}
public void tab(Events events) {
System.out.println("=======callback点击tab按钮:\n" + events);
}
}
public class KeybordTest {
public static void main(String[] args) {
KeybordCallback callback = new KeybordCallback();
Keybord keybord = new Keybord();
keybord.addListener(KeybordEventEnum.ENTER.getEventName(),callback);
keybord.addListener(KeybordEventEnum.CAPSLOCK.getEventName(),callback);
keybord.addListener(KeybordEventEnum.ESC.getEventName(),callback);
keybord.addListener(KeybordEventEnum.TAB.getEventName(),callback);
keybord.capsLock();
keybord.enter();
keybord.esc();
keybord.tab();
}
}
执行结果:
点击大写锁定按钮:
=======callback点击大写锁定按钮:
Events{
source=com.tealala.pattern.observer.events1.core.Keybord@31befd9f,
target=com.tealala.pattern.observer.events1.core.KeybordCallback@1c20c684,
callback=public void com.tealala.pattern.observer.events1.core.KeybordCallback.capsLock(com.tealala.pattern.observer.events1.core.Events),
trigger='capsLock',
time=Fri Dec 20 17:10:49 CST 2019
}
点击回车按钮:
=======callback点击回车按钮:
Events{
source=com.tealala.pattern.observer.events1.core.Keybord@31befd9f,
target=com.tealala.pattern.observer.events1.core.KeybordCallback@1c20c684,
callback=public void com.tealala.pattern.observer.events1.core.KeybordCallback.enter(com.tealala.pattern.observer.events1.core.Events),
trigger='enter',
time=Fri Dec 20 17:10:49 CST 2019
}
点击退出esc按钮:
=======callback点击退出esc按钮:
Events{
source=com.tealala.pattern.observer.events1.core.Keybord@31befd9f,
target=com.tealala.pattern.observer.events1.core.KeybordCallback@1c20c684,
callback=public void com.tealala.pattern.observer.events1.core.KeybordCallback.esc(com.tealala.pattern.observer.events1.core.Events),
trigger='esc',
time=Fri Dec 20 17:10:49 CST 2019
}
点击tab按钮:
=======callback点击tab按钮:
Events{
source=com.tealala.pattern.observer.events1.core.Keybord@31befd9f,
target=com.tealala.pattern.observer.events1.core.KeybordCallback@1c20c684,
callback=public void com.tealala.pattern.observer.events1.core.KeybordCallback.tab(com.tealala.pattern.observer.events1.core.Events),
trigger='tab',
time=Fri Dec 20 17:10:49 CST 2019
}