java设计模式详细讲解

Wesley13
• 阅读 684

原文链接:java设计模式详细讲解-观察者模式

如果想要更加详细的 Java中的23种设计模式视频资源,请点击链接:Java中的23种设计模式视频资源下载

1、观察者模式原理: 对象之间存在多对一的依赖院系,被依赖方向多个依赖放分发、发布事件时的一种程序设计解决方案,此时,被依赖方的对象称之为Subject,依赖放的对象称之为ObServ er,Subject向ObServer通知。

例如微信公众号的关注,关注者就是ObServer,被关注的公众号就是Subject。当关注者关账了一个公众号,那么公众号在推送文章时,就会想所有关注者推送这篇文章;反之,如果关注者取消关注,则公众号在推送文章时不再向他推送文章。类似的场景还有订牛奶等场景。

观察者模式中Subject对象一般包含如下事件:

1、添加事件(当有新的ObServer接入时调用)

2、移除事件(当有ObServer停止接入时调用)

3、通知/发布事件(当有新事件/消息发布时,通过此接口通知所有的ObServer)

而ObServer则提供如下事件/方法:

1、事件/消息接收(用于接收Subject推送的事件/消息)

Subject对象和ObServer对象的关系如下: java设计模式详细讲解

2、java 内置观察者 Java内置观察者和上述的观察者模式原理大致相同,

只是在java内置观察者模式中,被依赖对象称之为ObServable,且ObServable是一个类,而不是一个接口;开发者在使用的过程中,要继承这个类;并且由于ObServable已经实现了一部分事件,例如增加、移除、通知,我们在继承这个类之后,就无须考虑实现这些方法。

依赖的对象也称之为ObServer(注意,这个是一个接口,而不是一个类,之所以设计为接口,是因为在不同的项目实现的时候,在接收事件中要处理的逻辑可能不尽相同),

在ObServer观察者可以选择是等待ObServable直接推送消息内容,还是等待ObServable通知,然后自己主动向ObServable拉去消息,这是在Java内置观察者中的可以实现的。

Javan内置观察者添加ObServer后推送是先进后出,即先添加的会放在后面推送,而后进来的会先被调用通知。

3、观察者模式和Java内置观察者实现示例演示 3.1、观察者模式实现 由于观察者模式为自己实现,本案例将通过接口来实现

被观察者接口

package com.beBianMin.shareLogger.animal.ObserverMode.subject;

import com.beBianMin.shareLogger.animal.ObserverMode.observer.Observer; /**

  • 被观察者接口
  • @author 郭鹏飞

/ public interface Subject { /* * 注册新的观察者 * @param observer * @return */ public void registerObServer(Observer observer);

/**
 * 已拆除观察者
 * [@param](https://my.oschina.net/u/2303379) observer
 */
public void removeObserver(Observer observer);

/**
 * 通知观察者
 */
public void notifyObservers();

} 该接口有三个方法:注册、移除、通知

被观察者实现类:

package com.beBianMin.shareLogger.animal.ObserverMode.subject;

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

import com.beBianMin.shareLogger.animal.ObserverMode.observer.Observer;

/**

  • 被观察者实现
  • @author 郭鹏飞

*/ public class CurrentSubject implements Subject{

//参数1
private float temperatrue;
//参数2
private float pressure;
//参数3
private float humodity;


//设置一个观察者的列表
private List<Observer> observers;

public CurrentSubject() {
    observers = new ArrayList<Observer>();
}

public float getTemperatrue() {
    return temperatrue;
}
public void setTemperatrue(float temperatrue) {
    this.temperatrue = temperatrue;
}
public float getPressure() {
    return pressure;
}
public void setPressure(float pressure) {
    this.pressure = pressure;
}
public float getHumodity() {
    return humodity;
}
public void setHumodity(float humodity) {
    this.humodity = humodity;
}

public void setData(float temperatrue, float pressure, float humodity) {
    this.temperatrue = temperatrue;
    this.pressure = pressure;
    this.humodity = humodity;
    dataChage();
}

public void dataChage() {
    notifyObservers();
}

@Override
public void registerObServer(Observer observer) {
    observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
    if(observers.contains(observer)) {
        observers.remove(observer);
    }
}
@Override
public void notifyObservers() {
    for(Observer observer:observers) {
        observer.update(getTemperatrue(), getPressure(), getHumodity());
    }
}

} 这个类看看似有点乱,其实上面三个参数

//参数1 private float temperatrue; //参数2 private float pressure; //参数3 private float humodity; 是模拟要通知观察者的数据,这个可以随意设置,可以按照你需要通知的数据设置自己的对象格式。

在这个类里面要注意的是要设置一个用于存储观察者的容器对象并驶离话它

//设置一个观察者的列表 private List observers;

public CurrentSubject() { observers = new ArrayList(); } 在这个实现类中注册方法中,把新的观察者放入到这个容易中,在移除方法中把观察者从这个容器中移除掉,在通知方法中遍历所有的贯彻着,讲上面三个参数传递到观察者中。

并且我们现在假设在初始化上面所说的三个参数时,假设数据就是变化了,然后通过调用dataChage()方法来调用器notifyObservers方法来通知观察者。

观察者接口

package com.beBianMin.shareLogger.animal.ObserverMode.observer;

/**

  • 观察者接口
  • @author 郭鹏飞

*/ public interface Observer {

/**
 * 观察者被通知的方法
 * @param temperatrue
 * @param pressure
 * @param humodity
 */
public void update(float temperatrue,float pressure ,float humodity);

} 该接口只有一个接口,用于接收被观察者发布的数据

观察者试下你类,本案例模拟两个观察者来实现推送:

第一个观察者

package com.beBianMin.shareLogger.animal.ObserverMode.observer;

/**

  • 第一个观察者
  • @author 郭鹏飞

*/ public class CurrentObserver1 implements Observer{

@Override
public void update(float temperatrue, float pressure, float humodity) {
    System.out.println("第一个观察者被通知的结果,temperatrue:"+temperatrue);
    System.out.println("第一个观察者被通知的结果,pressure:"+pressure);
    System.out.println("第一个观察者被通知的结果,FloatArrayAssert:"+humodity);
}

} 第二个观察者

package com.beBianMin.shareLogger.animal.ObserverMode.observer;

/**

  • 第一个观察者
  • @author 郭鹏飞

*/ public class CurrentObserver2 implements Observer{

@Override
public void update(float temperatrue, float pressure, float humodity) {
    System.out.println("第二个观察者被通知的结果,temperatrue:"+temperatrue);
    System.out.println("第二个观察者被通知的结果,pressure:"+pressure);
    System.out.println("第二个观察者被通知的结果,FloatArrayAssert:"+humodity);
}

} 这两个观察者在实现的方法中只是打印了以下接收到的通知数据。

测试代码如下:

package com.beBianMin.shareLogger.animal.ObserverMode;

import com.beBianMin.shareLogger.animal.ObserverMode.observer.CurrentObserver1; import com.beBianMin.shareLogger.animal.ObserverMode.observer.CurrentObserver2; import com.beBianMin.shareLogger.animal.ObserverMode.subject.CurrentSubject;

public class TestClass { public static void main(String[] args) { CurrentObserver1 currentObserver1 ; CurrentObserver2 currentObserver2; CurrentSubject currentSubject ;

    System.out.println("测试添加除观察者=================");
    currentObserver1 = new CurrentObserver1();
    currentObserver2 = new CurrentObserver2();
    currentSubject = new CurrentSubject();
    currentSubject.registerObServer(currentObserver1);
    currentSubject.registerObServer(currentObserver2);
    
    currentSubject.setData(1, 2, 3);
    
    System.out.println("测试移除观察者=================");
    currentSubject.removeObserver(currentObserver2);
    currentSubject.setData(5, 6, 7);
}

} 运行代码结果如下:

测试添加除观察者================= 第一个观察者被通知的结果,temperatrue:1.0 第一个观察者被通知的结果,pressure:2.0 第一个观察者被通知的结果,FloatArrayAssert:3.0 第二个观察者被通知的结果,temperatrue:1.0 第二个观察者被通知的结果,pressure:2.0 第二个观察者被通知的结果,FloatArrayAssert:3.0 测试移除观察者================= 第一个观察者被通知的结果,temperatrue:5.0 第一个观察者被通知的结果,pressure:6.0 第一个观察者被通知的结果,FloatArrayAssert:7.0 发现第一次测试中,两个贯彻着都被通知到了,第二次测试中移除了第二个观察者,再次通知,只能通知到第一个观察者。并且发现一个现象:谁先被添加,谁先被通知(先进先出),这个现象在Java内置观察者中并不一致

3.2、Java内置观察者实现 在上面讲述Java内置观察者时咱们已经说过,Java内置了观察者(ObServable类)和被观察者(ObServer 接口),因此我们使用Java内置观察者实现观察者时就很简单。

由于Java内置观察者中被观察者向观察者通知时需要用一个对象来传递,因此我们定义一个作为传递数据的载体pojo:

package com.beBianMin.beBianMin.ObserverMode4Java.pojo;

public class Data4Java {

// 参数1
private float temperatrue;
// 参数2
private float pressure;
// 参数3
private float humodity;

public Data4Java(float temperatrue, float pressure, float humodity)
{
    this.temperatrue = temperatrue;
    this.pressure = pressure;
    this.humodity = humodity;
}

public float getTemperatrue()
{
    return temperatrue;
}

public void setTemperatrue(float temperatrue)
{
    this.temperatrue = temperatrue;
}

public float getPressure()
{
    return pressure;
}

public void setPressure(float pressure)
{
    this.pressure = pressure;
}

public float getHumodity()
{
    return humodity;
}

public void setHumodity(float humodity)
{
    this.humodity = humodity;
}

} 定义被观察者

package com.beBianMin.beBianMin.ObserverMode4Java.ObSeervable;

import java.util.Observable;

import com.beBianMin.beBianMin.ObserverMode4Java.pojo.Data4Java;

/**

  • 被观察者实现

  • @author 郭鹏飞 */ public class CurrentSubject extends Observable {

    // 参数1 private float temperatrue; // 参数2 private float pressure; // 参数3 private float humodity;

    public void setTemperatrue(float temperatrue) { this.temperatrue = temperatrue; }

    public float getPressure() { return pressure; }

    public void setPressure(float pressure) { this.pressure = pressure; }

    public float getHumodity() { return humodity; }

    public void setHumodity(float humodity) { this.humodity = humodity; }

    public void setData(float temperatrue, float pressure, float humodity) { this.temperatrue = temperatrue; this.pressure = pressure; this.humodity = humodity; dataChage(); }

    public void dataChage() { // setChanged方法会设置一个变量为true,它的作用是: // java内置观察者在通知的时候,会判断这个变量是不是为true,如果为true,则会通知,否则不会通知,而改制为人为false // 在同之前先调用setChanget()方法,表示数据是有更新的,时代吗更具有灵活性 // 你可以在通知代码中设置一个是否需要通知观察者的一个条件, // 例如当上面三个参数范围超过某个范围才会需要通知, // 这是就需要调用this.setChange()方法,表明参数右边,可以通知 this.setChanged(); // 方式1、只通知观察者,然后观察者调用被观察者拉去通知消息 // this.notifyObservers(); // 方式2、直接将一个对象传递给观察者 this.notifyObservers(new Data4Java(getHumodity(), getPressure(), getPressure())); }

} 注意上面的这个被观察者,由于上文咱们说了Java内置观察者(Observable ,为一个类)已经实现了添加、移除、通知观察者的方法,因此咱们就不想要重载这两个方法了,而只需要自己定义如何调用观察者逻辑即可,就是上面的setData方法

上面的这个观察者实现中要注意一下几点:

1、需要继承Observable类

2、不需要重载添加、移除、通知方法

3、通知观察者有两个方法/代码 notifyObservers()和notifyObservers(Object arg),这两个方法就是上面我说的你可以选择通知观察者数据有变化还是仅仅直接将要通知的内容传递进去

4、在调用3中的通知方法时,请首先调用setChanged();方法,这个很重要。原因在于,Java内置观察者在通知观察者之前,会通过一个内置的变量(true/false)来决定是否通知观察者,而这个变量默认为false(有兴趣的可以看下源码),如果你在调用3中的通知方法前不加此方法,Java内置观察则会将不会去通知观察者。这个方法的作用就是:让你的通知逻辑更加灵活(例如自己设置一个是否通知观察这的阈值)。

接下来实现第一个观察者:

package com.beBianMin.beBianMin.ObserverMode4Java.ObServer;

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

import com.beBianMin.beBianMin.ObserverMode4Java.pojo.Data4Java;

/**

  • 第一个观察者

  • @author 郭鹏飞 */ public class CurrentObserver1 implements Observer {

    @Override public void update(Observable o, Object arg) { // 观察者中将通知到的对象转换为实际对象 Data4Java data4Java = (Data4Java) arg;

     System.out.println("第一个观察者被通知的结果,temperatrue:" + data4Java.getTemperatrue());
     System.out.println("第一个观察者被通知的结果,pressure:" + data4Java.getPressure());
     System.out.println("第一个观察者被通知的结果,FloatArrayAssert:" + data4Java.getHumodity());
    

    }

} 第二个观察者:

package com.beBianMin.beBianMin.ObserverMode4Java.ObServer;

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

import com.beBianMin.beBianMin.ObserverMode4Java.pojo.Data4Java;

/**

  • 第二个观察者

  • @author 郭鹏飞 */ public class CurrentObserver2 implements Observer {

    @Override public void update(Observable o, Object arg) { // 观察者中将通知到的对象转换为实际对象 Data4Java data4Java = (Data4Java) arg;

     System.out.println("第二个观察者被通知的结果,temperatrue:" + data4Java.getTemperatrue());
     System.out.println("第二个观察者被通知的结果,pressure:" + data4Java.getPressure());
     System.out.println("第二个观察者被通知的结果,FloatArrayAssert:" + data4Java.getHumodity());
    

    }

} 上面咱们已经说了,观察者需要实现Observer接口(注意,是一个接口,之所以为接口,是因为他不知道你接收到数据后要干嘛,因此只能让你重载他的接收方法,然后你自己处理)

测试代码:

package com.beBianMin.beBianMin.ObserverMode4Java;

import com.beBianMin.beBianMin.ObserverMode4Java.ObSeervable.CurrentSubject; import com.beBianMin.beBianMin.ObserverMode4Java.ObServer.CurrentObserver1; import com.beBianMin.beBianMin.ObserverMode4Java.ObServer.CurrentObserver2;

public class TestClass { public static void main(String[] args) { CurrentObserver1 currentObserver1; CurrentObserver2 currentObserver2; CurrentSubject currentSubject;

    System.out.println("测试添加除观察者=================");
    currentObserver1 = new CurrentObserver1();
    currentObserver2 = new CurrentObserver2();
    currentSubject = new CurrentSubject();
    currentSubject.addObserver(currentObserver1);
    currentSubject.addObserver(currentObserver2);
    
    currentSubject.setData(1, 2, 3);
    
    System.out.println("测试移除观察者=================");
    currentSubject.deleteObserver(currentObserver2);
    currentSubject.setData(5, 6, 7);
}

} 运行结果如下:

测试添加除观察者================= 第二个观察者被通知的结果,temperatrue:3.0 第二个观察者被通知的结果,pressure:2.0 第二个观察者被通知的结果,FloatArrayAssert:2.0 第一个观察者被通知的结果,temperatrue:3.0 第一个观察者被通知的结果,pressure:2.0 第一个观察者被通知的结果,FloatArrayAssert:2.0 测试移除观察者================= 第一个观察者被通知的结果,temperatrue:7.0 第一个观察者被通知的结果,pressure:6.0 第一个观察者被通知的结果,FloatArrayAssert:6.0 通过上面打印出的结果,我们可以发现如下现象: 1、通知结果为先进后出,即谁先被添加进去,谁会被放在后面通知 2、被观察者不能多重继承(由于它已经继承了一个类),这是Java内置观察者的一个缺点 4、观察者模式的关键点 1、观察者模式实现了松偶尔、高内聚,高度隔离 2、使用java内置观察者可以省去添加、移除、通知的方法,缺点就是不容易定制化开发,无法实现多重继承

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java设计模式1
1:单例模式简介  单例模式是一种常用的软件设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供唯一的实例。总而言之就是在系统中只会存在一个对象,其中的数据是共享的  特点:    单例类只能有一个实例,所以一般会用static进行修释。    单例类必须自己创建自己的唯一实例。也就是在类中要new一个自己。    单例类必
浪人 浪人
3年前
java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
java泛型详解绝对是对泛型方法讲解最详细的,没有之一对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。本文参考、、1、概述泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。什么是泛型?
Wesley13 Wesley13
3年前
Java 设计模式(1)
设计模式(Designpattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
Java描述设计模式(11):观察者模式
本文源码:GitHub·点这里(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fcicadasmile%2Fmodelarithmeticparent)||GitEE·点这里(https://gitee.com/cicadasmile/modela
Wesley13 Wesley13
3年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这