当前位置: 代码迷 >> 综合 >> (二)设计模式之观察者模式(Observer)
  详细解决方案

(二)设计模式之观察者模式(Observer)

热度:80   发布时间:2023-12-14 16:54:49.0

今天来写一篇博客说明一下,设计模式中的观察者模式

(1)首先大家需要明白什么是观察者模式,其分为被观察者和观察者,如果被观察者的某一个状态发生改变,那么观察者都会收到相关信息!

(2)举个简单易懂的例子:

草原上草为绿色的时候,说明食草动物都会去,而食肉形动物也会去,这里的情况中

观察者(Observer)食草动物,食肉动物

被观察者(Observable)草地

(3)代码的实现:大家可以自己按照前面的逻辑实现该模式,但是java的API中也实现了观察者模式的相关接口与类,我个人还是喜欢先自己倒持倒持一下,玩一玩,看看能不能做出来,也能加深自己对该模式的理解

(4)(如果想直接看使用java的API来实现的请跳过该部分)首先这里我先用自己的代码来实现观察者模式,让大家明白简单的其原理,这里我采用上面的举例用代码实现。

    首先看一下效果,再看代码!

首先如果自己选择草地为绿色,并按下submit按钮,那么,羚羊和老虎都会去草原!

 

首先如果自己选择草地为黄色,并按下submit按钮,那么,羚羊和老虎都离开草原!

代码的具体实现:

(1)创建(被观察者接口)MyObervable

         方法:register(Myoberver   myObserver);------------------->对该被观察者添加观察对象

        方法:unRegister(Myoberver    myObserver);------------------->对该被观察者除去观察对象

        方法:notifyObservers();------------------->对该被观察者中的所有观察者通知(莫个状态以发生改变)

(2)创建(观察者接口)MyOberver

         方法:tackAction(MyObservable myObservable);将目前的被观察者加入到当前的观察者中,这样做的目的是,观察者可以得到被观察者对象,获取其中改变的状态!!(这里可能有一点不太好理解,但是大家看到代码就明白了!)

代码:

  (1)MyObservable(接口)(被观察者)

package MyObservable;import MyObserver.MyObserver;public interface MyObservable {//通知所有观察者public void notifyObservers();//对观察者进行注册public void register(MyObserver ob);//去掉某一个观察者public void unRegister(MyObserver ob);
}

 

(2)MyObserver(接口)(观察者)

package MyObserver;import MyObservable.MyObservable;public interface MyObserver {//将被观察者加入到观察者中public void takeAction(MyObservable myObservale);
}

(3)草地(被观察者)

package MyObservable;import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;import javax.swing.JComboBox;
import javax.swing.JFrame;import MyObserver.MyObserver;public class Plain extends JFrame implements ActionListener,MyObservable{private String plainState;private ArrayList<MyObserver> list = new ArrayList<MyObserver>();private JComboBox box;public String getPlainState() {return plainState;}public void setPlainState(String plainState) {this.plainState = plainState;}public ArrayList<MyObserver> getList() {return list;}public void setList(ArrayList<MyObserver> list) {this.list = list;}public Plain() {super("草原");setBounds(200, 200, 300, 300);setLayout(new FlowLayout());setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);Label lable = new Label("请选择草地的状态");Button submit = new Button("Submit");Button exit  = new Button("Exit");submit.addActionListener(this);exit.addActionListener(this);box = new JComboBox<String>();box.addItem("Green");box.addItem("Yellow");box.setVisible(true);box.addActionListener(this);add(lable);add(box);add(submit);add(exit);validate();}@Overridepublic void actionPerformed(ActionEvent e) {//如果提交,那么plain状态可能会发生改变//所以需要通知观察该对象的所有观察者if(e.getActionCommand().equals("Submit")) {plainState = (String)box.getSelectedItem();notifyObservers();} if(e.getActionCommand().equals("Exit")) {this.dispose();}}@Overridepublic void register(MyObserver ob) {list.add(ob);}@Overridepublic void unRegister(MyObserver ob) {list.remove(ob);}@Override//通知观察者就是把当前的被观察者对象传过去//然后再观察者那边获得该对象中的plainSate状态public void notifyObservers() {for (MyObserver myObserver : list) {myObserver.takeAction(this);}}}

(4)羚羊(观察者)

package MyObserver;import java.awt.FlowLayout;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.JFrame;import MyObservable.MyObservable;
import MyObservable.Plain;public class Antelopes extends JFrame implements MyObserver{//羚羊的状态private String antelopesState;private String currentPlainState;//最新的草地状态private TextArea area = new TextArea();public String getAntelopesState() {return antelopesState;}public void setAntelopesState(String antelopesState) {this.antelopesState = antelopesState;}public Antelopes() {super("羚羊");setBounds(200, 200, 500, 500);setLayout(new FlowLayout());setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);add(area);}@Overridepublic void takeAction(MyObservable myObservale) {//得到当前的草地的状态currentPlainState =  ((Plain) myObservale).getPlainState();if(currentPlainState.equals("Green")) {area.setText("草地状态:Green!  羚羊的状态:去草原!");}else if(currentPlainState.equals("Yellow")) {area.setText("草地状态:Yellow!  羚羊的状态:离开草原!");}else {area.setText("");}}}

(4)老虎(观察者)

package MyObserver;import java.awt.FlowLayout;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.JFrame;import MyObservable.MyObservable;
import MyObservable.Plain;public class Tigers extends JFrame implements MyObserver{private String currentPlainState;//最新的草地状态private TextArea area = new TextArea();public Tigers() {super("老虎");setBounds(200, 200, 500, 500);setLayout(new FlowLayout());setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);add(area);}@Overridepublic void takeAction(MyObservable myObservale) {//得到当前的草地的状态currentPlainState =  ((Plain) myObservale).getPlainState();if(currentPlainState.equals("Green")) {area.setText("草地状态:Green!  老虎的状态:去草原!");}else if(currentPlainState.equals("Yellow")) {area.setText("草地状态:Yellow!  老虎的状态:离开草原!");}else {area.setText("");}}}

(5)运行代码(main)

package Test;import MyObservable.Plain;
import MyObserver.Antelopes;
import MyObserver.Tigers;public class TestMyObserverMode {public static void main(String[] args) {//创建被观察者对象Plain plain = new Plain();//创建观察者对象一---->antelopes,并对被观察者添加观察者Antelopes antelopes = new Antelopes();plain.register(antelopes);//创建观察者对象二---->tigers,并对被观察者添加观察者Tigers tigers = new Tigers();plain.register(tigers);}}

以上就是使用自己的代码来实现观察者模式


接下来使用java提供的API实现观察者模式,那废话我就不多说了,说一些要点(大家一定要阅读这里。。。。。。。。。。。。。。)

(1)观察者(依然是一个接口   import java.util.Observer),

(2)但是被观察者是一个类(import java.util.Observable;)(其中的方法都已实现,大家只需要继承它即可)

(3)java.util.Observable的方法有:

            addObserver(Observer  observer);添加观察者与之前我自己写代码中的register()方法一样,其中的观察者存储在容器中,和我的一样,大家可以去看一下源码!

           deleteObserver(Observer  observer);删除某个观察者。

           notifyObservers(Object arg);通知所有观察者。参数的意思是被观察者的那个状态发生了改变!

           setChanged()说明被观察者的某一个状态已经改变

(4) import java.util.Observer的方法有:(就一个,很寒酸,不用调用,再使用notifyObservers(Object arg)和 setChanged()会自动调用!)

           void update(Observable o, Object arg);第一个参数的意思是哪一个被观察者,第二个那个状态发生了改变!

(5)其中在plant类中一旦状态发生改变一定要调用setChanged()再调用notifyObservers(Object arg),把被观察者改变的状态对象发给观察者!并且此时update()函数会自动调用,其参数也会被赋值,为什么?请看源码:

 public void notifyObservers(Object arg) {/** a temporary array buffer, used as a snapshot of the state of* current Observers.*/Object[] arrLocal;synchronized (this) {/* We don't want the Observer doing callbacks into* arbitrary code while holding its own Monitor.* The code where we extract each Observable from* the Vector and store the state of the Observer* needs synchronization, but notifying observers* does not (should not).  The worst result of any* potential race-condition here is that:* 1) a newly-added Observer will miss a*   notification in progress* 2) a recently unregistered Observer will be*   wrongly notified when it doesn't care*/if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}

现在知道观察者为什么不用调用了吧!!所以这里有一点非常重要,就是在被观察者的状态发生改变,一定要先调用setChange(),在调用notifyObservers(Object arg),否则源码中的change状态是false,将不能通知观察者!!

(6)代码部分:

      (6.1)被观察者(Observable)

       

package APIObservable;import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;import javax.swing.JComboBox;
import javax.swing.JFrame;
/*** 使用java自带的api实现观察者模式* @author xiaomi**/
public class Plain extends Observable implements ActionListener{private JComboBox box = new JComboBox<String>();public Plain() {JFrame frame = new JFrame("草地");frame.setBounds(200,200,300,300);frame.setVisible(true);frame.setLayout(new FlowLayout());frame.setDefaultCloseOperation(3);box.addItem("Green");box.addItem("Yellow");Button submit = new Button("Submit");Button exit = new Button("Exit");submit.addActionListener(this);exit.addActionListener(this);box.addActionListener(this);frame.add(box);frame.add(submit);frame.add(exit);frame.validate();}@Overridepublic void actionPerformed(ActionEvent e) {if(e.getActionCommand().equals("Submit")) {//草地状态发生改变String plianState = (String)box.getSelectedItem();System.out.println(plianState);//说明状态以改变,并通知所有观察者(把改变的对象传过去)//然后再观察者中的uodate中的2个参数就会有值了,并自动调用setChanged();notifyObservers(plianState);}}}

   (6.2)观察者(tigers)

package APIObserver;import java.awt.FlowLayout;
import java.awt.TextArea;
import java.util.Observable;
import java.util.Observer;import javax.swing.JFrame;/*** 使用java自己提供的API实现观察者模式* @author xiaomi**/
public class Tigers extends JFrame implements Observer {TextArea area = new TextArea();public Tigers() {super("老虎");setBounds(200, 200, 500, 500);setLayout(new FlowLayout());setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);add(area);}/*** 当被观察者plain调用addOberver时,此时的*/public void update(Observable o, Object arg) {//得到当前的草地状态String plainState = (String)arg;if(plainState.equals("Green")) {area.setText("草地状态:Green!!    羚羊状态:去吃草!!");}else if(plainState.equals("Yellow")) {area.setText("草地状态:Yellow!!    羚羊状态:离开草原!!");}else {area.setText("");}}}

(6.3)观察者(antelopes)羚羊

package APIObserver;import java.awt.FlowLayout;
import java.awt.TextArea;
import java.util.Observable;
import java.util.Observer;import javax.swing.JFrame;/*** 使用java自己提供的API实现观察者模式* @author xiaomi**/
public class Antelopes extends JFrame implements Observer{TextArea area = new TextArea();public Antelopes() {super("羚羊");setBounds(200, 200, 500, 500);setLayout(new FlowLayout());setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);add(area);}/*** 当被观察者plain调用addOberver时,此时的*/public void update(Observable o, Object arg) {//得到当前的草地状态String plainState = (String)arg;if(plainState.equals("Green")) {area.setText("草地状态:Green!!    羚羊状态:去吃草!!");}else if(plainState.equals("Yellow")) {area.setText("草地状态:Yellow!!    羚羊状态:离开草原!!");}else {area.setText("");}}}

应用场景:

 1、订单: 如果现在是有一个用户买了一个商品,我们会给这个用户送一些积分,还有短信的发送,还要记录这个用户插入数据库的订单日志等等,如果是平时的话,我们需要在一个请求里面,添加这些逻辑,代码,但是假如后面运营那边,频繁改动,那么这个逻辑代码就需要频繁增加修改,而且需要修改整个逻辑  ,而现在使用观察者模式的话,下订单就是一个被观察者,短信的发送、数据库的订单日志、送一些积分都是被观察者,一旦下订单成功,通知这些被观察者即可,后面只需增加或者修改订单观察者的类即可,如果以后决定不需要送积分,直接在下订单这个别观察者中去掉送积分这个观察者即可

 

 

 

  相关解决方案