Basic Framework
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
LoginEvent.java
package Observer_Pattern;
import java.util.EventObject;
public class LoginEvent extends EventObject {
//LonginEvent表示事件类,它用于封装与事件有关的信息。它不是观察者模式的一部分,但它可以在目标对象和观察者对象之间传递数据
private String userName;
private String passWord;
public LoginEvent( Object source, String userName, String passWord ) {
super( source );
this.userName = userName;
this.passWord = passWord;
}
public void setUserName( String userName ) {
this.userName = userName;
}
public String getUserName( ) {
return this.userName;
}
public void setPassWord( String passWord ) {
this.passWord = passWord;
}
public String getPassWord( ) {
return passWord;
}
}
LoginEventListener.java
package Observer_Pattern;
import java.util.EventListener;
public interface LoginEventListener extends EventListener {
//抽象观察者(登录事件监听器)
public void validateLogin( LoginEvent event );//声明响应方法
}
LoginBean.java
package Observer_Pattern;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class LoginBean extends JPanel implements ActionListener {
//LoginBean充当具体目标类(登录控件)
JLabel labUserName;
JLabel labPassWord;
JTextField txtUserName;
JPasswordField txtPassWord;
JButton btnLogin;
JButton btnClear;
LoginEventListener lel;//定义一个抽象观察者对象
LoginEvent le;//定义一个事件对象,用于传输数据
public LoginBean() {
this.setLayout( new GridLayout(3,2) );
labUserName = new JLabel( "User Name:" );
add( labUserName );
txtUserName = new JTextField( 20 );
add( txtUserName );
labPassWord = new JLabel( "PassWord:" );
add( labPassWord );
txtPassWord = new JPasswordField( 20 );
add( txtPassWord );
btnLogin = new JButton( "Login" );
add( btnLogin );
btnClear = new JButton( "Clear" );
add( btnClear );
btnClear.addActionListener( this );
btnLogin.addActionListener( this );
}
//实现注册方法
void addLoginEventListener( LoginEventListener lel ) {
this.lel = lel;
}
//实现通知方法
private void fireLoginEvent( Object object, String userName, String PassWord ) {
le = new LoginEvent( btnLogin, userName, PassWord );
lel.validateLogin( le );
}
public void actionPerformed( ActionEvent event ) {
if( btnLogin == event.getSource() ){
String userName = this.txtUserName.getText();
String PassWord = this.txtPassWord.getText();
fireLoginEvent( btnLogin, userName, PassWord );
}
if( btnClear == event.getSource() ){
this.txtUserName.setText( "" );
this.txtPassWord.setText( "" );
}
}
}
LoginValidatorA.java
package Observer_Pattern;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class LoginValidatorA extends JFrame implements LoginEventListener {
//具体观察者类
//判断用户名和密码是否为空
private JPanel p;
private LoginBean lb;//定义具体目标
private JLabel lbLogo;
public LoginValidatorA() {
super( "GitHub" );
p = new JPanel();
this.getContentPane().add( p );
lb = new LoginBean();
lb.addLoginEventListener( this );//调用目标对象的注册方法
Font f = new Font( "Times New Roman", Font.BOLD, 30 );
lbLogo = new JLabel( "GitHub" );
lbLogo.setForeground( Color.blue );
p.setLayout( new GridLayout( 2,1 ) );
p.add( lbLogo );
p.add( lb );
p.setBackground( Color.pink );
this.setSize( 600,200 );
this.setVisible( true );
}
//实现在抽象观察者中声明的相应方法
public void validateLogin( LoginEvent event ){
String userName = event.getUserName();
String passWord = event.getPassWord();
if( 0 == userName.trim().length() || 0 == passWord.trim().length() ){
JOptionPane.showMessageDialog( this, new String(" UserName or PassWord is empty! "), "alert", JOptionPane.ERROR_MESSAGE );
}
else{
JOptionPane.showMessageDialog( this, new String(" Valid Login Info! "),"alert",JOptionPane.INFORMATION_MESSAGE );
}
}
public static void main( String args[] ){
new LoginValidatorA().setVisible( true );
}
}
LoginValidatorB.java
package Observer_Pattern;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class LoginValidatorB extends JFrame implements LoginEventListener {
//具体观察者类
//判断用户名和密码是否相同
private JPanel p;
private LoginBean lb;//定义具体目标
private JLabel lbLogo;
public LoginValidatorB() {
super( "FaceBook" );
p = new JPanel();
this.getContentPane().add( p );
lb = new LoginBean();
lb.addLoginEventListener( this );//调用目标对象的注册方法
Font f = new Font( "Times New Roman", Font.BOLD, 30 );
lbLogo = new JLabel( "FaceBook" );
lbLogo.setForeground( Color.red );
p.setLayout( new GridLayout( 2,1 ) );
p.add( lbLogo );
p.add( lb );
p.setBackground( new Color(163, 185, 255) );
this.setSize( 600,200 );
this.setVisible( true );
}
//实现在抽象观察者中声明的相应方法
public void validateLogin( LoginEvent event ){
String userName = event.getUserName();
String passWord = event.getPassWord();
if( userName.equals( passWord ) ){
JOptionPane.showMessageDialog( this, new String(" UserName must be different from passWord! "), "alert", JOptionPane.ERROR_MESSAGE );
}
else{
JOptionPane.showMessageDialog( this, new String(" Right Details! "),"alert",JOptionPane.INFORMATION_MESSAGE );
}
}
public static void main( String args[] ){
new LoginValidatorB().setVisible( true );
}
}
Running Effect
Source Download
Please click the address->Observer Pattern
Summarize
Usage Scenario
- 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
Advantage
- 除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
Disadvantage
- 在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。