观察者模式
需求:JinSanPang放烟花,Korea和Japan派间谍观察并报告总部
- 简单实现:
public class Japan {
public void toukui(String message){
System.out.println("Japan偷窥到JSP"+message);
}
}
public class Korea {
public void jianting(String message){
System.out.println("Korea监听到JSP"+message);
}
}
public class JinSanPang {
private Japan japan;
private Korea korea;
public void setJapan(Japan japan) {
this.japan = japan;
}
public void setKorea(Korea korea) {
this.korea = korea;
}
public void fangYanHua(String name){
System.out.println("放了一个烟花:"+name);
korea.jianting(name);
japan.toukui(name);
}
}
public class TestMain {
public static void main(String[] args){
JinSanPang jsp = new JinSanPang();
//两国间谍
Japan japan = new Japan();
Korea korea = new Korea();
//两国派遣间谍
jsp.setJapan(japan);
jsp.setKorea(korea);
//JSP放烟花
jsp.fangYanHua("放烟花");
}
}
运行结果:
- 监听者优化为接口实现:
上述写法生产者消费者之间的耦合度过高,新消费者的加入以及已存在消费者的退出都需要修改代码。
消费者的不同不影响消费者此处功能的相同,很明显这个功能可以抽出来。另:功能的相同不影响实现过程的不同。(你用支付宝还是微信都是付钱,总是不能不付钱的。。。)
所以,抽成接口更合适。
public interface JianDie {
void observe(String message);
}
public class Korea implements JianDie {
@Override
public void observe(String message) {
System.out.println("Korea监听到JSP"+message);
}
}
public class Japan implements JianDie {
@Override
public void observe(String message) {
System.out.println("Japan偷窥到JSP"+message);
}
}
public class JinSanPang {
private JianDie jianDie;
public void setJianDie(JianDie jianDie) {
this.jianDie = jianDie;
}
public void fangYanHua(String name){
System.out.println("放了一个烟花:"+name);
jianDie.observe(name);
}
}
public class TestMain {
public static void main(String[] args){
JinSanPang jsp = new JinSanPang();
//两国间谍
Korea korea = new Korea();
Japan japan = new Japan();
//两国派遣间谍
jsp.setJianDie(korea);
jsp.setJianDie(japan);
//JSP放烟花
jsp.fangYanHua("放烟花");
}
}
运行结果:
- 监听者增删的优化:
上述写法生产者中只有一个消费者对象,后到的消费者对象会覆盖前面的消费者对象。
此时只能有一个消费者得到生产者的产品,需进行修改。
public class JinSanPang {
//不要忘了初始化集合,原来只有一个座位,谁来是谁,现在需要很多座位,谁来给谁一个
private static List<JianDie> jianDies = new ArrayList<>();
public static void registry(JianDie jianDie) {
jianDies.add(jianDie);
}
public static void unregistry(JianDie jianDie){
jianDies.remove(jianDie);
}
public void fangYanHua(String name){
System.out.println("放了一个烟花:"+name);
for (JianDie jd : jianDies) {
jd.observe(name);
}
}
}
public class TestMain {
public static void main(String[] args){
JinSanPang jsp = new JinSanPang();
//两国间谍
Korea korea = new Korea();
Japan japan = new Japan();
//两国派遣间谍
jsp.registry(korea);
jsp.registry(japan);
//JSP放烟花
jsp.fangYanHua("放烟花");
jsp.unregistry(japan);
jsp.fangYanHua("放hedan1号");
}
}
测试结果:
生产者存在之后,消费者初始化时需要调用该生产对象的注册方法把自己注入后一直观察。这样此消费者初始化后,生产者生产的所有产品消费者都能获取。
Java的事件监听机制
Java的事件监听机制基于观察者设计模式,进行了更高层次的抽象封装
1、事件监听涉及到三个组件:事件源、事件参数、事件监听器
2、当事件源上发生某一个动作时,它会调用事件监听器的一个方法,并在调用该方法时把事件参数对象传递进去,
开发人员在监听器中通过事件参数对象,就可以拿到事件源,从而对事件源进行操作。
public class Demo {
public static void main(String[] args) {
Person p = new Person();
p.registerListener(new PersonListener() {
@Override
public void doeat(Event e) {
Person p = e.getSource();
System.out.println(p + "吃啥呢");
}
@Override
public void dorun(Event e) {
}
});
p.eat();
}
}
class Person {
private PersonListener listener;
public void eat() {
if (listener != null) {
listener.doeat(new Event(this));
}
}
public void run() {
if (listener != null) {
listener.dorun(new Event(this));
}
}
public void registerListener(PersonListener listener) {
this.listener = listener;
}
}
interface PersonListener {
void doeat(Event e);
void dorun(Event e);
}
class Event {
private Person source;
public Event() {
super();
}
public Event(Person source) {
super();
this.source = source;
}
public Person getSource() {
return source;
}
public void setSource(Person source) {
this.source = source;
}
}
GUI编程中事件监听
public static void main(String[] args) {
Frame f = new Frame();
f.setSize(400, 400);
f.setVisible(true);
f.addWindowListener(new WindowListener(){
public void windowActivated(WindowEvent e) {
// TODO Auto-generated method stub
}
public void windowClosed(WindowEvent e) {
// TODO Auto-generated method stub
}
public void windowClosing(WindowEvent e) {
System.out.println("美死你!!");
Frame f = (Frame) e.getSource();
f.dispose();
}
public void windowDeactivated(WindowEvent e) {
// TODO Auto-generated method stub
}
public void windowDeiconified(WindowEvent e) {
// TODO Auto-generated method stub
}
public void windowIconified(WindowEvent e) {
// TODO Auto-generated method stub
}
public void windowOpened(WindowEvent e) {
// TODO Auto-generated method stub
}
});
}
Servlet编程中事件监听
Spring 中事件监听
- 需求 / 解决问题:
class A {
public static void main(String[] args) {
/*
同一 应用/程序/单机 中两个类之间通信方法?
面向对象:A类持有B类对象,直接调用B类方法
*/
B b = new B();
b.aaa("A调用B的方法,本地类之间相互通信");
/*
/*
以上方式耦合性较高
如何做到A不持有B中对象且能触发B方法的执行
*/
}
}
class B {
void aaa(String aaa) {
System.out.println(aaa);
}
}
- 依赖:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
</dependencies>
- 示例:
@Data
public class Abc {
private String name;
private Integer age;
}
@Configuration
@ComponentScan
public class B {
@EventListener//事件的监听器
//此方法在哪儿写都行,记得加@Component
public void aaa(Abc abc){
System.err.println("B===>"+abc);
}
}
@Component
public class C {
@EventListener
public void aaaa(Abc abc){
System.out.println("C===>"+abc);
}
}
- 使用:
public class A {
public static void main(String[] args){
/*
Spring中event事件:需传入所需对象并且无返回值的方法被多处使用时,用于解耦
*/
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(B.class);
Abc abc = new Abc();
abc.setName("张三");
abc.setAge(10);
context.publishEvent(abc);//把三胖发布出去,大家一来就都有message
//一定要是没有返回值,不会抛异常的方法,否则context把对象送出去的时候就混乱了
}
}