观察者模式

干完活抽点时间写一下第二个,观察者模式,定义如下:

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

这里用例子说话,某动物园里,饲养员给动物们提供了一份食物报刊,动物们感兴趣的可以自己订阅,订阅后有食物变动都会通知它们。
观察者模式中,被观察者需要实现一个主题抽象(模式中讲的抽象可以是抽象类或者接口),而这里的主题就是食物报刊;而观察者要实现观察者抽象,这里的观察者就是动物们。
主题和观察者抽象代码如下:

package head_first.observer.self;

public interface Subject {
    public void registerObserver(Observer o);//注册观察者
    public void removeObserver(Observer o);//取消订阅
    public void notifyObservers();//通知观察者
}

package head_first.observer.self;

public interface Observer {
    public void update(String name, int number);
}

具体主题(被观察者)食物报刊如下:

package head_first.observer.self;

import java.util.ArrayList;

public class FoodData implements Subject {
    ArrayList<Observer> observers;//主题队列
    String name; //食物
    int number; //食物数量

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

    /**
     * 注册观察者
     */
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    /**
     * 取消观察者
     */
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0)
            observers.remove(o);
    }

    /**
     * 通知观察者
     */
    public void notifyObservers() {
        for(int i=0;i<observers.size();i++){
            Observer o = observers.get(i);
            o.update(name, number);
        }
    }

    private void measurementsChanged(){
        notifyObservers();
    }

    /**
    *更新食物消息
    */
    public void setMesurements(String name, int number){
        this.name = name;
        this.number = number;
        measurementsChanged();//更新消息后通知观察者
    }
}

然后是观察者具体类老鼠和狗:

package head_first.observer.self;

import head_first.observer.weatherwatch.DisplayElement;

public class MouseObserver implements DisplayElement, Observer {
    private String name; // 食物
    private int number; // 食物数量
    private Subject foodData;

    public MouseObserver(Subject foodData) {
        this.foodData = foodData;
        this.foodData.registerObserver(this);
    }

    public void display() {
        System.out.println("I'm mouse : the name of food is: " + name + ", number is: "
                + number);
    }

    public void update(String name, int number) {
        this.name = name;
        this.number = number;
        display();
    }

    public void removeSubject(){
        this.foodData.removeObserver(this);
    }

}
package head_first.observer.self;

import head_first.observer.weatherwatch.DisplayElement;

/**
*  DisplayElement是观察者的一个消息通知模板接口
*/
public class DogObserver implements DisplayElement, Observer {
    // 订阅者想知道的东西
    private String name;
    private int number;
    private Subject foodData;

    public DogObserver(Subject foodData) {
        this.foodData = foodData;
        this.foodData.registerObserver(this);
    }

    public void update(String name, int number) {
        this.name = name;
        this.number = number;
        display();
    }

    /**
    *  收到的消息
    */
    public void display() {
        System.out.println("I'm dog : the name of food is: " + name + ", number is: "
                + number);
    }

    /**
    *  退订报刊
    */
    public void removeSubject(){
        this.foodData.removeObserver(this);
    }

}

DisplayElement代码:

package head_first.observer.weatherwatch;

public interface DisplayElement {
    public void display();
}

接下来让我们测试一下吧:

package head_first.observer.self;

public class FoodTest {
    public static void main(String[] args) {
        FoodData foodData = new FoodData();
        //老鼠和狗都订阅了食物报刊
        MouseObserver mouse = new MouseObserver(foodData);
        DogObserver dog = new DogObserver(foodData);

        foodData.setMesurements("apple", 3);
        foodData.setMesurements("Dog Food", 5);

        System.out.println("--------------------");
        //总是没有喜欢吃的,老鼠退订了
        mouse.removeSubject();
        foodData.setMesurements("rice", 4); //有稻米了,可惜老鼠不知道咯~
    }
}

这是运行结果:

I'm mouse : the name of food is: apple, number is: 3
I'm dog : the name of food is: apple, number is: 3
I'm mouse : the name of food is: Dog Food, number is: 5
I'm dog : the name of food is: Dog Food, number is: 5
--------------------
I'm dog : the name of food is: rice, number is: 4

之前老鼠和狗都能收到消息,可是老鼠退订后就收不到消息了,自然也错过了稻米的美味~~

利用观察者模式,主题是拥有状态的对象,并且可以控制这些状态。
而观察者使用这些状态,虽然这些状态并不属于他们,有许多观察者依赖主题来告诉他们状态何时改变。

主题是真正拥有数据的人,观察者依赖于主题,在数据变化时更新,这样便是实现了观察者与具体数据之间的解耦和,是一种更干净的OO设计。

设计原则
为了交互对象之间的松耦合设计而努力。
———————松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

当然,jdk里有java内置的观察者模式
java.util.Observer和java.util.Observable。不过跟上述例子的差异是Observable是类。

JAVA内置的观察者模式运作方式:
如何把对象变成观察者:
观察者实现观察者接口(java.util.Observer),然后调用Observable对象的addObserver()方法,退订是调用deleteObserver()即可。
被观察者如何送出通知:
首先,扩展java.util.Observable类,然后
1、先调用setChanged()方法,标记状态已经改变的事实。
2、调用两种notifyObservers()方法其中之一:
notifyObservers()或 notifyObservers(Object arg)

观察者接受通知:
观察者实现了更新的方法update(Observable o, Object arg),以主题对象为第一个参数,可以让观察者知道哪个主题通知他的,arg就是notifyObservers(Object arg)中的参数。

java.util.Observable的黑暗面:
Observable是一个类,它违反了“针对抽象编程,而非针对实现编程”的原则,限制了使用和复用,它的子类必须继承它,但此时如果想同时具有另一个超类的行为,便没办法了,java不支持多继承。
还有Observable类中有些方法被定义成protected(比如setChanged()),这就意味着只有继承Observable,才能创建Observable的实例并组合到你自己的对象中,违反了“多用组合,少用继承”的原则。

另外,swing和GUI中也有好多观察者模式的使用,可以好好研究下。

【使用教程】 一、环境配置 1、建议下载anaconda和pycharm 在anaconda中配置好环境,然后直接导入到pycharm中,在pycharm中运行项目 anaconda和pycharm安装及环境配置参考网上博客,有很多博主介绍 2、在anacodna中安装requirements.txt中的软件包 命令为:pip install -r requirements.txt 或者改成清华源后再执行以上命令,这样安装要快一些 软件包都安装成功后才算成功 3、安装好软件包后,把anaconda中对应的python导入到pycharm中即可(不难,参考网上博客) 二、环境配置好后,开始训练(也可以训练自己数据集) 1、数据集准备 需要准备yolo格式的目标检测数据集,如果不清楚yolo数据集格式,或者有其他数据训练需求,请看博主yolo格式各种数据集集合链接:https://blog.csdn.net/DeepLearning_/article/details/127276492 里面涵盖了上百种yolo数据集,且在不断更新,基本都是实际项目使用。来自于网上收集、实际场景采集制作等,自己使用labelimg标注工具标注的。数据集质量绝对有保证! 本项目所使用的数据集,见csdn该资源下载页面中的介绍栏,里面有对应的下载链接,下载后可直接使用。 2、数据准备好,开始修改配置文件 参考代码中data文件夹下的banana_ripe.yaml,可以自己新建一个不同名称的yaml文件 train:训练集的图片路径 val:验证集的图片路径 names: 0: very-ripe 类别1 1: immature 类别2 2: mid-ripe 类别3 格式按照banana_ripe.yaml照葫芦画瓢就行,不需要过多参考网上的 3、修改train_dual.py中的配置参数,开始训练模型 方式一: 修改点: a.--weights参数,填入'yolov9-s.pt',博主训练的是yolov9-s,根据自己需求可自定义 b.--cfg参数,填入 models/detect/yolov9-c.yaml c.--data参数,填入data/banana_ripe.yaml,可自定义自己的yaml路径 d.--hyp参数,填入hyp.scratch-high.yaml e.--epochs参数,填入100或者200都行,根据自己的数据集可改 f.--batch-size参数,根据自己的电脑性能(显存大小)自定义修改 g.--device参数,一张显卡的话,就填0。没显卡,使用cpu训练,就填cpu h.--close-mosaic参数,填入15 以上修改好,直接pycharm中运行train_dual.py开始训练 方式二: 命令行方式,在pycharm中的终端窗口输入如下命令,可根据自己情况修改参数 官方示例:python train_dual.py --workers 8 --device 0 --batch 16 --data data/coco.yaml --img 640 --cfg models/detect/yolov9-c.yaml --weights '' --name yolov9-c --hyp hyp.scratch-high.yaml --min-items 0 --epochs 500 --close-mosaic 15 训练完会在runs/train文件下生成对应的训练文件及模型,后续测试可以拿来用。 三、测试 1、训练完,测试 修改detect_dual.py中的参数 --weights,改成上面训练得到的best.pt对应的路径 --source,需要测试的数据图片存放的位置,代码中的test_imgs --conf-thres,置信度阈值,自定义修改 --iou-thres,iou阈值,自定义修改 其他默认即可 pycharm中运行detect_dual.py 在runs/detect文件夹下存放检测结果图片或者视频 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值