java-设计模式-观察者模式

简介: 简单观察者模式观察者模式中,一个被观察者管理所有依赖它的观察者,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

简单观察者模式

观察者模式中,一个被观察者管理所有依赖它的观察者,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
角色
抽象被观察者角色:把所有对观察者对象的引用保存在一个集合中,每个被观察者角色都可以有任意数量的观察者。被观察者提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
具体被观察者角色:在被观察者内部状态改变时,给所有登记过的观察者发出通知。具体被观察者角色通常用一个子类实现。
具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
适用场景
1) 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
2) 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
3) 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的

public interface Watcher  
{  
     public void update();  
}  
public interface Watched  
{  
     public void addWatcher(Watcher watcher);  

     public void removeWatcher(Watcher watcher);  

     public void notifyWatchers();  
}  
public class Thief implements Watcher  
{  
     @Override  
     public void update()  
     {  
          System.out.println(“运输车有行动,强盗准备动手");  
     }  
}  
public class Police implements Watcher  
{  
     @Override  
     public void update()  
     {  
          System.out.println(“运输车有行动,警察护航");  
     }  
}  
public class Transporter implements Watched  
{  
     private List<Watcher> list = new ArrayList<Watcher>();  

     @Override  
     public void addWatcher(Watcher watcher)  
     {  
          list.add(watcher);  
     }  

     @Override  
     public void removeWatcher(Watcher watcher)  
     {  
          list.remove(watcher);  
     }  

     @Override  
     public void notifyWatchers(String str)  
     {  
          for (Watcher watcher : list)  
          {  
               watcher.update();  
          }  
     }  

}  
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
public class Test  
{  
     public static void main(String[] args)  
     {  
          Transporter transporter = new Transporter();  

          Police police = new Police();  
          Security security = new Security();  
          Thief thief = new Thief();  

          transporter.addWatcher(police);  
          transporter.addWatcher(security);  
          transporter.addWatcher(security);  

          transporter.notifyWatchers();  
     }  
}  

推模型和拉模型
在观察者模式中,又分为推模型和拉模型两种方式。
  ●  推模型
     主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
  ●  拉模型
     主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

最近做一个消息系统,其中涉及到新消息数的即时更新,当时就想到了观察者模式,后来听同事提到推拉模式,感觉推模式原理上应该还是属于观察者模式,只不过把server变成了被观察对象,client被动观察 .
其实推拉模式我们经常遇到,如广播(推)、HTTP请求(拉),只是没有刻意去追求概念。设计时还是应该多考虑到。
考虑到性能还效率,最终还是选择了拉模式,每隔一断时间请求一次、更新。

下面是引用:
推(push)模式是一种基于客户器/服务器机制、由服务器主动将信息送到客户器的技术。在push模式应用中,服务器把信息送给客户器之前,并没有明显的客户请求。push事务由服务器发起。push模式可以让信息主动、快速地寻找用户/客户器,信息的主动性和实时性比较好。但精确性较差,可能推送的信息并不一定满足客户的需求。推送模式不能保证能把信息送到客户器,因为推模式采用了广播机制,如果客户器正好联网并且和服务器在同一个频道上,推送模式才是有效的。push模式无法跟踪状态,采用了开环控制模式,没有用户反馈信息。在实际应用中,由客户器向服务器发送一个申请,并把自己的地址(如IP、port)告知服务器,然后服务器就源源不断地把信息推送到指定地址。在多媒体信息广播中也采用了推模式。另外,如手机*、qq广播。

拉(pull)模式与推模式相反,是由客户器主动发起的事务。服务器把自己所拥有的信息放在指定地址(如IP、port),客户器向指定地址发送请求,把自己需要的资源“拉”回来。不仅可以准确获取自己需要的资源,还可以及时把客户端的状态反馈给服务器。
使用jdk的java.util.Observable

import java.util.Observable;
/**
 * 天气目标的具体实现类
 * @author lenovo
 *
 */
public class ConcreatWeatherSubject extends Observable{

    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
        //通知所有的观察者
        this.setChanged();
        //推模型:主动通知
        this.notifyObservers(content);
        //this.notifyObservers();
    }

}
import java.util.Observable;
import java.util.Observer;

public class ConcreatObserver implements Observer {

    private String observerName;

    public String getObserverName() {
        return observerName;
    }

    public void setObserverName(String observerName) {
        this.observerName = observerName;
    }

    /**
     * Observable o 拉取方式
     * Object arg   推的方式,推具体的内容过来
     */
    @Override
    public void update(Observable o, Object arg) {
        System.out.println(this.observerName+"收到推送消息:"+arg);
        System.out.println(this.observerName+"拉去消息中的内容:"+((ConcreatWeatherSubject)o).getContent());
    }
}
public class Client {
    public static void main(String[] args) {
        ConcreatWeatherSubject subject= new ConcreatWeatherSubject();
        ConcreatObserver girl = new ConcreatObserver();
        girl.setObserverName("king");
        ConcreatObserver girl1 = new ConcreatObserver();
        girl1.setObserverName("jinhang");
        ConcreatObserver girl2 = new ConcreatObserver();
        girl2.setObserverName("kee");
        subject.addObserver(girl1);
        subject.addObserver(girl);
        subject.addObserver(girl2);
        subject.setContent("天气晴朗!");

    }
}

条件消息系统

import java.util.ArrayList;
import java.util.List;

public abstract class WeatherSubject {

    public List<Observer> observers = new ArrayList<Observer>();

    public void attach(Observer o) {
        observers.add(o);
    }

    public void dettach(Observer o) {
        observers.remove(o);
    }

    protected abstract void notifyObservers();
}
import java.util.Iterator;

public class Subject1 extends WeatherSubject{

    private String weatherContent;

    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
        this.notifyObservers();
    }

    @Override
    protected void notifyObservers() {
        for(Observer o: observers){
            //通知条件
            if("下雨".equals(this.weatherContent)){
                System.out.println("notifyObservers");
                if("jinhang".equals(o.getObserverName())){
                    o.update(this);
                }
                if("kee".equals(o.getObserverName())){
                    o.update(this);
                }
            }
            if("下雪".equals(this.weatherContent)){
                if("king".equals(o.getObserverName())){
                    o.update(this);
                }
            }

        }
    }
}
public interface Observer {

    public void update(WeatherSubject subject);
    public void setObserverName(String observerName);
    public String getObserverName();
}
public class Observer1 implements Observer{

    private  String observerName;
    private  String weatherContent;
    private  String remindContet;
    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
    }

    public String getRemindContet() {
        return remindContet;
    }

    public void setRemindContet(String remindContet) {
        this.remindContet = remindContet;
    }

    @Override
    public void update(WeatherSubject subject) {
        System.out.println("start...............");
        weatherContent = ((Subject1)subject).getWeatherContent();
        System.out.println(observerName+"收到消息"+weatherContent+"准备去:"+remindContet);
    }

    @Override
    public void setObserverName(String observerName) {
        this.observerName = observerName;
    }

    @Override
    public String getObserverName() {
        return observerName;
    }

}
public class Test {
    public static void main(String[] args) {
        Subject1 subject1 = new Subject1();
        Observer1 observer1 = new Observer1();
        Observer1 observer2 = new Observer1();
        observer1.setObserverName("jinhang");
        observer1.setRemindContet("购物");
        observer2.setObserverName("kee");
        observer2.setRemindContet("不出去了");
        subject1.attach(observer1);
        subject1.attach(observer2);
        subject1.setWeatherContent("下雨");
    }
}

JUnit为用户提供了三种不同的测试结果显示界面,以后还可能会有其它方式的现实界面……。怎么才能将测试的业务逻辑和显示结果的界面很好的分离开?不用问,就是观察者模式!

public interface TestListener {
       /**
       * An error occurred.
       */
       public void addError(Test test, Throwable t);
       /**
       * A failure occurred.
       */
      public void addFailure(Test test, AssertionFailedError t); 
       /**
        * A test ended.
        */
      public void endTest(Test test);
       /**
        * A test started.
        */
       public void startTest(Test test);
}
//具体观察者角色,我们采用最简单的TextUI下的情况来说明(AWT,Swing对于整天做Web应用的人来说,已经很陌生了)
public class ResultPrinter implements TestListener {
       //省略好多啊,主要是显示代码
……
       //下面就是实现接口TestListener的四个方法
       //填充方法的行为很简单的说
       /**
        * @see junit.framework.TestListener#addError(Test, Throwable)
        */
       public void addError(Test test, Throwable t) {
              getWriter().print("E");
       }
       /**
        * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
        */
       public void addFailure(Test test, AssertionFailedError t) {
              getWriter().print("F");
       }
       /**
        * @see junit.framework.TestListener#endTest(Test)
        */
       public void endTest(Test test) {
       }
       /**
        * @see junit.framework.TestListener#startTest(Test)
        */
       public void startTest(Test test) {
              getWriter().print(".");
              if (fColumn++ >= 40) {
                     getWriter().println();
                     fColumn= 0;
              }
       }
}


来看下我们的目标角色,随便说下,由于JUnit功能的简单,只有一个目标——TestResult,因此JUnit只有一个具体目标角色。
//好长的代码,好像没有重点。去掉了大部分与主题无关的信息
//下面只列出了当Failures发生时是怎么来通知观察者的
public class TestResult extends Object {
       //这个是用来存放测试Failures的集合
protected Vector fFailures;
//这个就是用来存放注册进来的观察者的集合
       protected Vector fListeners;


       public TestResult() {
              fFailures= new Vector();
              fListeners= new Vector();
       }
       /**
        * Adds a failure to the list of failures. The passed in exception
        * caused the failure.
        */
       public synchronized void addFailure(Test test, AssertionFailedError t) {
              fFailures.addElement(new TestFailure(test, t));
              //下面就是通知各个观察者的addFailure方法
              for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
                     ((TestListener)e.nextElement()).addFailure(test, t);
              }
       }
       /**
        * 注册一个观察者
        */
       public synchronized void addListener(TestListener listener) {
              fListeners.addElement(listener);
       }
       /**
        * 删除一个观察者
        */
       public synchronized void removeListener(TestListener listener) {
              fListeners.removeElement(listener);
       }
       /**
        * 返回一个观察者集合的拷贝,当然是为了防止对观察者集合的非法方式操作了
     * 可以看到所有使用观察者集合的地方都通过它
        */
       private synchronized Vector cloneListeners() {
              return (Vector)fListeners.clone();
       }
       ……
}
察者模式组成所需要的角色在这里已经全了。不过好像还是缺点什么……。呵呵,对!就是它们之间还没有真正的建立联系。在JUnit中是通过TestRunner来作的,而你在具体的系统中可以灵活掌握。
看一下TestRunner中的代码:
public class TestRunner extends BaseTestRunner {
       private ResultPrinter fPrinter;
public TestResult doRun(Test suite, boolean wait) {
//就是在这里注册的
              result.addListener(fPrinter);
目录
相关文章
|
14天前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
1天前
|
设计模式 Java
【JAVA基础篇教学】第十四篇:Java中设计模式
【JAVA基础篇教学】第十四篇:Java中设计模式
|
3天前
|
设计模式 算法 Java
设计模式在Java开发中的应用
设计模式在Java开发中的应用
15 0
|
9天前
|
设计模式 前端开发 Java
19:Web开发模式与MVC设计模式-Java Web
19:Web开发模式与MVC设计模式-Java Web
20 4
|
9天前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
25 4
|
10天前
|
设计模式 缓存 监控
JAVA设计模式之结构型模式
结构模型:适配器模型、桥接模型、过滤器模型、组合模型、装饰器模型、外观模型、享受元模型和代理模型。
21 3
|
13天前
|
设计模式 算法 Java
Java基础教程(19)-设计模式简述
【4月更文挑战第19天】设计模式是软件设计中反复使用的代码设计经验,旨在提升代码的可重用性、可扩展性和可维护性。23种模式分为创建型、结构型和行为型三类。创建型模式如工厂方法、抽象工厂、建造者、原型和单例,关注对象创建与使用的分离。结构型模式涉及对象组合,如适配器、装饰器、外观等,增强结构灵活性。行为型模式专注于对象间职责分配和算法合作,包括责任链、命令、观察者等。设计模式提供标准化解决方案,促进代码交流和复用。
|
14天前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
21 1
|
14天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
35 2
|
14天前
|
设计模式 算法 Java
Java 设计模式:深入模板方法模式的原理与应用
【4月更文挑战第27天】模板方法模式是一种行为设计模式,主要用于定义一个操作中的算法的框架,允许子类在不改变算法结构的情况下重定义算法的某些特定步骤。
22 1