学习笔记-观察者模式

观察者模式

没有营养的话:

在学习观察者模式的时候

观察者模式分为两大部分

被观察者和观察者

对于这两大部分,可以在生活中找到很多对应的关系,视观察的对象对场景有很大的不一样

比如,被观察者是微博的大V,那么观察者就是关注他的粉丝,大V发出动态,他的粉丝就会收到反馈

被观察者是一名嫌疑犯的时候,那么观察者就是警察,嫌疑犯有任何可疑的动静的时候,警察都可以收到反馈

之前在学习观察者模式的时候,容易和消息中间件的那种生产订阅模式混淆,其实他们不一样

和生产订阅的那种模式很像但是又没有那种模式那么解耦

观察者模式由于只有两个部分,所以对于观察者的维护,就在被观察者的对象中,会维护一个观察者的集合

而生产消费模式中,生产只负责生产,消费只负责消费,中间的需求关系由另外的Broker进行维护,实现真正意义上的解耦

观察者模式的实现

结构

  1. 观察者接口
  2. 被观察对象的接口
  3. 观察者接口的实现
  4. 被观察对象接口的实现
  5. 主类测试

观察者的接口

package it.luke.observer;

/**
 * 观察者接口
 */
public interface ObserverInter {
    /**
     * 针对某个被观察对象的消息更新
     * @param message
     * @param name
     */
    void update(String message,String name);
    
}

被观察对象的接口

package it.luke.observer;

/**
 * 观察的目标接口
 */
public interface TargetInter {

    //    添加观察者
    TargetInter addObserver(ObserverInter Observer);

    //    移除观察者
    void removeObserver(ObserverInter Observer);

    //    通知观察者
    void notice(String message);

}

观察者的实现

package it.luke.observer;

/**
 * 观察者1
 */
public class Observer_v1 implements ObserverInter {
    private String name = "观察者1";
    private String message;


    //用于主类调用的时候观察反馈
    //这里的参数name是被观察者的name
    @Override
    public void update(String message, String name) {

        this.message = message;
        System.out.println(this.name + "收到反馈:" + name + "更新了事件:" + message);
    }
}

/**
 * 观察者2
 */
class Observer_v2 implements ObserverInter {
    private String name = "观察者2";
    private String message;

    @Override
    public void update(String message, String name) {
        this.message = message;
        System.out.println(this.name + "收到反馈:" + name + "更新了事件:" + message);

    }
}

/**
 * 观察者3
 */
class Observer_v3 implements ObserverInter {
    private String name = "观察者3";
    private String message;

    @Override
    public void update(String message, String name) {
        this.message = message;
        System.out.println(this.name + "收到反馈:" + name + "更新了事件:" + message);
    }
}

主类测试

package it.luke.observer;

public class MainTest {
    public static void main(String[] args) {

        TargetImpl target = new TargetImpl();
        Observer_v1 observer_v1 = new Observer_v1();
        Observer_v2 observer_v2 = new Observer_v2();
        Observer_v3 observer_v3 = new Observer_v3();


        //添加观察者
        target.addObserver(observer_v1).addObserver(observer_v2).addObserver(observer_v3);

        //模拟场景,三名观察者观察着对象,接收对象的反馈信息
        //场景1:嫌疑犯伸出他罪恶的手,企图从他人的包里窃取金钱!!!
        target.notice("嫌疑犯伸出他罪恶的手,企图从他人的包里窃取金钱!!!");


    }
}




观察者1收到反馈:target更新了事件:嫌疑犯伸出他罪恶的手,企图从他人的包里窃取金钱!!!
观察者2收到反馈:target更新了事件:嫌疑犯伸出他罪恶的手,企图从他人的包里窃取金钱!!!
观察者3收到反馈:target更新了事件:嫌疑犯伸出他罪恶的手,企图从他人的包里窃取金钱!!!

Process finished with exit code 0

可疑看到,当我们通过调用被观察者对象的notice()方法进行信息刷新的时候,由维护着观察者集合的被观察者调用update()方法,进行信息的反馈

除了我们自己的实现的观察者模式,java还有自己实现的观察者模式

java 的观察者的接口

/*
 * Copyright (c) 1994, 1998, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

观察者的实现类

package it.luke.observer.java_observer;

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

public class Oberver_v1 implements Observer {

    private String name = "观察者1";


    @Override
    public void update(Observable o, Object arg) {
        Target o1 = (Target) o;
        String TarName = o1.getName();
        System.out.println(name+"收到"+TarName+"反馈:"+arg.toString());
    }
}

观察对象的接口(主要的更新方法)

 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);
    }

其它的方法和自定义的差不多,多了一些校验

观察对象的实现类

package it.luke.observer.java_observer;

import java.util.Observable;

public class Target extends Observable {
    private String name ="嫌疑犯";

    public String getName() {
        return name;
    }

    @Override
    protected synchronized void setChanged() {
        super.setChanged();
    }
}

重写setChanged方法主要是java内置的更新会进行一个是否更新的判断,而且是protected修饰的,所以可疑通过重写的方式,手动调用来进行更新.

了解了观察者模式的大概之后,便可以以这个基础来学习用法更为广泛的生产订阅模式

这里引用大数据最常用的消息中间件(Kafka)不仅高吞吐量,而且快,支持分布式

在学习Kafka之前,同样的为了可以更好的看到代码的走向,建议将Kafka的源码拉下来构建

  1. 拉取Kafka源码,依旧是建议去码云上拉,比较是国内的,速度更快(kafka源码)
  2. 准备构建工具 gradle
  3. 构建(这里我采用的是idea,直接将项目导入idea,可以适当的修改build.gradle 中的仓库位置,减少构建时间)
    在这里插入图片描述
    显示成功;
    验证:
    平时我们在启动kafka的时候是在linux中使用命令行的形式,启动的kafka-server
    这里主要是调用了kafka源码中的core模块的中kafka启动类,通过传入配置参数(config/server.properites)进行启动
    在这里插入图片描述
    kafka默认依赖于zookeeper,
    在启动kafka之前,需要启动zookeeper,而在kafka源码中自带了zookeeper组件
    可以进入到源码目录中,通过cmd的模式–>bin\zookeeper-server-start.sh config\zookeeper.properties
    正常启动之后,默认开启了localhost:2181作为zookeeper的端口
    在这里插入图片描述

接下来可以启动kafkaserver了—>以run configuration 的形式启动(配置参数见上方)
在这里插入图片描述
画面进入这里,没有报错,说明启动成功了,这里可以通过调用官方提供的样例代码来执行,看看是否构建完成
在这里插入图片描述

可以直接执行,如果报错,可以通过cmd查看你的topic是否存在,因为他的默认提供了三个测试的topic(topic1,topic2,topic3),如果不存在,可以通过命令行的形式创建topic

bin\windows\kafka-topics.bat --create --topic topic1 --replication factor 1 --partitions 1 --zookeeper localhost:2181

执行创建对应的topic
创建完之后继续执行

public void run() {
    int messageNo = 1;
    while (true) {
        String messageStr = "Message_" + messageNo;
        long startTime = System.currentTimeMillis();
        if (isAsync) { // Send asynchronously
            producer.send(new ProducerRecord<>(topic,
                messageNo,
                messageStr), new DemoCallBack(startTime, messageNo, messageStr));
        } else { // Send synchronously
            try {
                producer.send(new ProducerRecord<>(topic,
                    messageNo,
                    messageStr)).get();
                System.out.println("Sent message: (" + messageNo + ", " + messageStr + ")");
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        ++messageNo;
    }

producer 调用run方法,向指定的topic发送了自己模拟的数据
然后又调用了consumer进行消费,我们可以在控制台看到对应的输出

在这里插入图片描述
到这里说明我们的源码已经构建完毕

待补充…

1.Kafka 通过C\S架构,将客户端和服务端分离开来,其中涉及到的client访问服务端的线程并发问题,源码架构采用了
Reacotr模型(待补充…)
2.Kafka 服务端的启动原理(待补充…)
3.kafka 客户端的连接注册原理(待补充…)
4.kafka 生产消费原理(待补充…)

Reacotr模型主要分为三个角色

Reactor:把IO事件分配给对应的handler处理

Acceptor:处理客户端连接事件

Handler:处理非阻塞的任务

传统阻塞IO模型的不足

1:每个连接都创建一个线程去提供处理,如果遇到高并发情况下,创建的线程过多,会导致资源的浪费

2:如果采用阻塞的方式,连接建立之后,如果当前线程没有数据可读,线程便会阻塞在当前线程上,造成资源浪费

参考数据库连接池的案例,采用线程池的思想,可以有效的缓解高并发下,创建线程过多的问题

线程复用,如果当前无数据,会回收线程,或者一个线程处理多个连接,可以有效的避免线程占用带来的资源浪费.

Reactor线程模型的思想就是基于IO复用和线程池的结合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《Python学习笔记》是由皮大庆编写的一本关于Python语言学习的教材。在这本书中,作者详细介绍了Python语言的基础知识、语法规则以及常用的编程技巧。 首先,作者简要介绍了Python语言的特点和优势。他提到,Python是一种易于学习和使用的编程语言,受到了广大程序员的喜爱。Python具有简洁、清晰的语法结构,使得代码可读性极高,同时也提供了丰富的库和模块,能够快速实现各种功能。 接着,作者详细讲解了Python的基本语法。他从变量、数据类型、运算符等基础知识开始,逐步介绍了条件语句、循环控制、函数、模块等高级概念。同时,作者通过大量的示例代码和实践案例,帮助读者加深对Python编程的理解和应用。 在书中,作者还特别强调了编写规范和良好的编程习惯。他从命名规范、注释风格、代码缩进等方面指导读者如何写出清晰、可读性强的Python代码。作者认为,良好的编程习惯对于提高代码质量和提高工作效率非常重要。 此外,作者还介绍了Python的常用库和模块。他提到了一些常用的库,如Numpy、Pandas、Matplotlib等。这些库在数据处理、科学计算、可视化等领域有广泛的应用,帮助读者更好地解决实际问题。 总的来说,《Python学习笔记》是一本非常实用和全面的Python学习教材。通过学习这本书,读者可以系统地学习和掌握Python编程的基础知识和高级应用技巧,为以后的编程学习和工作打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值