观察者模式指定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式主要是解决一个对象状态改变给其他对象通知的问题。总共分为四部分:
- 抽象被观察者角色:抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。
- 抽象观察者角色:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,在得到主题通知时更新自己。
- 具体被观察者角色:具体的主题,在集体主题的内部状态改变时,所有订阅过的观察者发出通知。
- 具体观察者角色:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
实现
抽象观察者角色
public interface Observer {
public void updateInfo(String message);
}
具体观察者角色
public class ManObserver implements Observer {
@Override
public void updateInfo(String message) {
System.out.println("男士接受的信息:"+message);
}
}
=====
public class WomanObserver implements Observer {
@Override
public void updateInfo(String message) {
System.out.println("女士接受的信息:"+message);
}
}
抽象被观察者角色
public interface SubInfoService {
//发送消息
public void subMessage(String message);
//订阅
public void addObservers(Observer observer);
//取消订阅
public void cancerObservers(Observer observer);
}
具体被观察者角色
import java.util.ArrayList;
import java.util.List;
public class SubInfo implements SubInfoService{
private List<Observer> observers;
private String message;
public SubInfo(){
observers = new ArrayList<Observer>();
}
//发布信息
public void subMessage(String message){
this.message=message;
notifyAllObserver();
}
//订阅
public void addObservers(Observer observer){
observers.add(observer);
}
//每个订阅者都发布信息
public void notifyAllObserver(){
for (Observer observer : observers) {
observer.updateInfo(message);
}
}
//取消订阅
@Override
public void cancerObservers(Observer observer) {
observers.remove(observer);
}
}
测试
public class TestSub {
public static void main(String[] args) {
WomanObserver wo = new WomanObserver();
ManObserver mo = new ManObserver();
SubInfo sub = new SubInfo();
sub.addObservers(mo);
sub.addObservers(wo);
sub.subMessage("信息来了");
System.out.println("=======");
sub.subMessage("你们好啊!");
System.out.println("=======");
sub.cancerObservers(wo);
sub.subMessage("我们聊聊");
}
}
结果
男士接受的信息:信息来了
女士接受的信息:信息来了
=======
男士接受的信息:你们好啊!
女士接受的信息:你们好啊!
=======
男士接受的信息:我们聊聊
这个观察者模式优点就是观察者和被观察者是抽象耦合的。缺点就是当订阅者过多的时候,所有观察者通知会花费很多时间。如果观察者和被观察者之间循环调用就会使得系统崩溃。
在Spring的事件机制中就体现了观察者模式。
我在SSM的项目实现这样的功能,当用户注册的时候,发送一封邮件给该用户。
EmailEvent.java
import org.springframework.context.ApplicationEvent;
public class EmailEvent extends ApplicationEvent{
private static final long serialVersionUID = -5948806306069839382L;
private String emailAddress;
public EmailEvent(String emailAddress) {
super(emailAddress);
this.emailAddress =emailAddress;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}
EmailEventListener.java
import org.simplejavamail.email.Email;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.mailer.Mailer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class EmailEventListener implements ApplicationListener<EmailEvent> {
private static Logger log = LoggerFactory.getLogger(EmailEventListener.class);
@Autowired
private Mailer mailer;
@Value("${user.email.name}")
private String emailName;
@Override
public void onApplicationEvent(EmailEvent event) {
String email = event.getEmailAddress();
log.info("事件监听,获取到注册邮件是:"+email);
//发送邮件
Email em = EmailBuilder.startingBlank()
.from("商家",emailName)
.to("用户", email)
.withSubject("请查收你的邮件")
.withPlainText("非常感谢你的注册,快来逛一逛我们网站吧")
.buildEmail();
mailer.sendMail(em);
}
}
EmailEventPublish.java
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
@Component
public class EmailEventPublish implements ApplicationEventPublisherAware{
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher=applicationEventPublisher;
}
public void publish(String email){
EmailEvent event = new EmailEvent(email);
applicationEventPublisher.publishEvent(event);
}
}
applicationContext.xml
<context:component-scan base-package="com.plf" />
UserController.java
@Autowired
private ApplicationContext applicationContext;
public String register(User user){
...
applicationContext.publishEvent(new EmailEvent(user.getEmail()));
...
}