共计 6605 个字符,预计需要花费 17 分钟才能阅读完成。
本文节选自《设计模式就该这样学》
1 基于 Java API 实现告诉机制
当小伙伴们在社区发问时,如果有设置指定用户答复,则对应的用户就会收到邮件告诉,这就是观察者模式的一种利用场景。有些小伙伴可能会想到 MQ、异步队列等,其实 JDK 自身就提供这样的 API。咱们用代码来还原这样一个利用场景,首先创立 GPer 类。
/**
* JDK 提供的一种观察者的实现形式,被观察者
*/
public class GPer extends Observable{
private String name = "GPer 生态圈";
private static GPer gper = null;
private GPer(){}
public static GPer getInstance(){if(null == gper){gper = new GPer();
}
return gper;
}
public String getName() {return name;}
public void publishQuestion(Question question){System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。");
setChanged();
notifyObservers(question);
}
}
而后创立问题 Question 类。
public class Question {
private String userName;
private String content;
public String getUserName() {return userName;}
public void setUserName(String userName) {this.userName = userName;}
public String getContent() {return content;}
public void setContent(String content) {this.content = content;}
}
接着创立老师 Teacher 类。
public class Teacher implements Observer {
private String name;
public Teacher(String name) {this.name = name;}
public void update(Observable o, Object arg) {GPer gper = (GPer)o;
Question question = (Question)arg;
System.out.println("======================");
System.out.println(name + "老师,你好!\n" +
"您收到了一个来自" + gper.getName() + "的发问,心愿您解答。问题内容如下:\n" +
question.getContent() + "\n" + "提问者:" + question.getUserName());
}
}
最初编写客户端测试代码。
public static void main(String[] args) {GPer gper = GPer.getInstance();
Teacher tom = new Teacher("Tom");
Teacher jerry = new Teacher("Jerry");
gper.addObserver(tom);
gper.addObserver(jerry);
// 用户行为
Question question = new Question();
question.setUserName("张三");
question.setContent("观察者模式实用于哪些场景?");
gper.publishQuestion(question);
}
运行后果如下图所示。
2 基于 Guava API 轻松落地观察者模式
笔者向大家举荐一个实现观察者模式的十分好用的框架,API 应用也非常简单,举个例子,首先引入 Maven 依赖包。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
而后创立侦听事件 GuavaEvent。
/**
* Created by Tom
*/
public class GuavaEvent {
@Subscribe
public void subscribe(String str){
// 业务逻辑
System.out.println("执行 subscribe 办法, 传入的参数是:" + str);
}
}
最初编写客户端测试代码。
/**
* Created by Tom
*/
public class GuavaEventTest {public static void main(String[] args) {EventBus eventbus = new EventBus();
GuavaEvent guavaEvent = new GuavaEvent();
eventbus.register(guavaEvent);
eventbus.post("Tom");
}
}
3 应用观察者模式设计鼠标事件响应 API
再来设计一个业务场景,帮忙小伙伴们更好地了解观察者模式。在 JDK 源码中,观察者模式的利用也十分多。例如 java.awt.Event 就是观察者模式的一种,只不过 Java 很少被用来写桌面程序。咱们用代码来实现一下,以帮忙小伙伴们更粗浅地理解观察者模式的实现原理。首先,创立 EventListener 接口。
/**
* 观察者形象
* Created by Tom.
*/
public interface EventListener {
}
创立 Event 类。
/**
* 规范事件源格局的定义
* Created by Tom.
*/
public class Event {
// 事件源,动作是由谁收回的
private Object source;
// 事件触发,要告诉谁(观察者)private EventListener target;
// 观察者的回应
private Method callback;
// 事件的名称
private String trigger;
// 事件的触发事件
private long time;
public Event(EventListener target, Method callback) {
this.target = target;
this.callback = callback;
}
public Object getSource() {return source;}
public Event setSource(Object source) {
this.source = source;
return this;
}
public String getTrigger() {return trigger;}
public Event setTrigger(String trigger) {
this.trigger = trigger;
return this;
}
public long getTime() {return time;}
public Event setTime(long time) {
this.time = time;
return this;
}
public Method getCallback() {return callback;}
public EventListener getTarget() {return target;}
@Override
public String toString() {
return "Event{" +
"source=" + source +
", target=" + target +
", callback=" + callback +
", trigger='" + trigger + '\'' +
", time=" + time +
'}';
}
}
创立 EventContext 类。
/**
* 被观察者的形象
* Created by Tom.
*/
public abstract class EventContext {protected Map<String,Event> events = new HashMap<String,Event>();
public void addListener(String eventType, EventListener target, Method callback){events.put(eventType,new Event(target,callback));
}
public void addListener(String eventType, EventListener target){
try {
this.addListener(eventType, target,
target.getClass().getMethod("on"+toUpperFirstCase(eventType), Event.class));
}catch (NoSuchMethodException e){return;}
}
private String toUpperFirstCase(String eventType) {char [] chars = eventType.toCharArray();
chars[0] -= 32;
return String.valueOf(chars);
}
private void trigger(Event event){event.setSource(this);
event.setTime(System.currentTimeMillis());
try {if (event.getCallback() != null) {
// 用反射调用回调函数
event.getCallback().invoke(event.getTarget(), event);
}
}catch (Exception e){e.printStackTrace();
}
}
protected void trigger(String trigger){if(!this.events.containsKey(trigger)){return;}
trigger(this.events.get(trigger).setTrigger(trigger));
}
}
而后创立 MouseEventType 接口。
/**
* Created by Tom.
*/
public interface MouseEventType {
// 单击
String ON_CLICK = "click";
// 双击
String ON_DOUBLE_CLICK = "doubleClick";
// 弹起
String ON_UP = "up";
// 按下
String ON_DOWN = "down";
// 挪动
String ON_MOVE = "move";
// 滚动
String ON_WHEEL = "wheel";
// 悬停
String ON_OVER = "over";
// 失去焦点
String ON_BLUR = "blur";
// 取得焦点
String ON_FOCUS = "focus";
}
创立 Mouse 类。
/**
* 具体的被观察者
* Created by Tom.
*/
public class Mouse extends EventContext {public void click(){System.out.println("调用单击办法");
this.trigger(MouseEventType.ON_CLICK);
}
public void doubleClick(){System.out.println("调用双击办法");
this.trigger(MouseEventType.ON_DOUBLE_CLICK);
}
public void up(){System.out.println("调用弹起办法");
this.trigger(MouseEventType.ON_UP);
}
public void down(){System.out.println("调用按下办法");
this.trigger(MouseEventType.ON_DOWN);
}
public void move(){System.out.println("调用挪动办法");
this.trigger(MouseEventType.ON_MOVE);
}
public void wheel(){System.out.println("调用滚动办法");
this.trigger(MouseEventType.ON_WHEEL);
}
public void over(){System.out.println("调用悬停办法");
this.trigger(MouseEventType.ON_OVER);
}
public void blur(){System.out.println("调用取得焦点办法");
this.trigger(MouseEventType.ON_BLUR);
}
public void focus(){System.out.println("调用失去焦点办法");
this.trigger(MouseEventType.ON_FOCUS);
}
}
创立回调办法 MouseEventLisenter 类。
/**
* 观察者
* Created by Tom.
*/
public class MouseEventListener implements EventListener {public void onClick(Event e){System.out.println("=========== 触发鼠标单击事件 ==========" + "\n" + e);
}
public void onDoubleClick(Event e){System.out.println("=========== 触发鼠标双击事件 ==========" + "\n" + e);
}
public void onUp(Event e){System.out.println("=========== 触发鼠标弹起事件 ==========" + "\n" + e);
}
public void onDown(Event e){System.out.println("=========== 触发鼠标按下事件 ==========" + "\n" + e);
}
public void onMove(Event e){System.out.println("=========== 触发鼠标挪动事件 ==========" + "\n" + e);
}
public void onWheel(Event e){System.out.println("=========== 触发鼠标滚动事件 ==========" + "\n" + e);
}
public void onOver(Event e){System.out.println("=========== 触发鼠标悬停事件 ==========" + "\n" + e);
}
public void onBlur(Event e){System.out.println("=========== 触发鼠标失去焦点事件 ==========" + "\n" + e);
}
public void onFocus(Event e){System.out.println("=========== 触发鼠标取得焦点事件 ==========" + "\n" + e);
}
}
最初编写客户端测试代码。
public static void main(String[] args) {EventListener listener = new MouseEventListener();
Mouse mouse = new Mouse();
mouse.addListener(MouseEventType.ON_CLICK,listener);
mouse.addListener(MouseEventType.ON_MOVE,listener);
mouse.click();
mouse.move();}
关注微信公众号『Tom 弹架构』回复“设计模式”可获取残缺源码。
【举荐】Tom 弹架构:30 个设计模式实在案例(附源码),挑战年薪 60W 不是梦
本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!
如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『Tom 弹架构』可获取更多技术干货!