观察者模式详解_观察者模式属于什么模式

观察者模式详解_观察者模式属于什么模式本篇文章总结一下观察者模式,主要从以上几点介绍。 观察模式是我们在开发过程中经常遇到的一种设计模式,这里先来介绍一下概念。 从字面意思上去理解,所谓的观察者模式,首先有观察者(一个或者多个),被观察者(一个)。当被观察者状态发生变化的时候,就会去通知它的所有的观察者,然后由观察…

观察者模式详解_观察者模式属于什么模式

本篇文章总结一下观察者模式,主要从以上几点介绍。

声明

概念介绍

观察模式是我们在开发过程中经常遇到的一种设计模式,这里先来介绍一下概念。

从字面意思上去理解,所谓的观察者模式,首先有观察者(一个或者多个),被观察者(一个)。当被观察者状态发生变化的时候,就会去通知它的所有的观察者,然后由观察者根据被观察者的情况作出反应。观察者模式属于行为型模式。

在 Android 中的接口回调属于一种特殊的观察者模式,观察者只有一个(监听器)。

在观察者模式中有以下四个重要的角色:

Subject 被观察者的抽象接口,在这个接口里面,提供添加观察者和删除观察者、通知观察者的方法。

ConcreteSubject 具体的被观察者,Subject 的实现类。在具体的内部发生改变时,给所有注册过的观察者发送通知。

Observer 观察者的抽象接口,在这个接口里面定义了一个更新方法,当收到被观察者的通知的时候就更新。

ConcreteObserver 具体的观察者,Observer 的实现类。实现 Observer 的更新方法。

图例解释

使用送奶工的图例来介绍:

观察者模式详解_观察者模式属于什么模式

送奶工就可以看做是一个被观察者,然后订奶的张三、赵四、王五就是观察者(观察送奶工有没有来),送奶工来送奶会通知各位观察者,通知他们来取奶。这个时候某六也想订奶,就可以告诉送奶工,以后他也要订奶了,以后来送奶的时候就也会通知某六了。

观察者模式详解_观察者模式属于什么模式

观察者模式详解_观察者模式属于什么模式

某一天,张三不想喝奶了,就告诉送奶工,他不再订奶了,那么以后送奶工再来送奶就不会通知张三了。

具体的代码实现

根据上面的概念,需要有一个被观察者的抽象,也就是送奶工的抽象

public interface Subject{
    // 注册观察者
    void registerObserver(Observer o);
    // 取消注册观察者
    void unregisterObserver(Observer o);
    // 通知观察者
    void notifyObserver();
}

具体的观察者对象

public class ConcreteSubject implements Subject{
    // 存放观察者的集合
    private List<Observer> observers;
    public ConcreteSubject(){
        observers = new ArrayList<>();
    }
    // 注册观察者
    @Override
    public void registerObserver(Observer o){
        if(!observers.contains(o)){
            observers.add(o);
        }
    }
    // 取消注册观察者
    @Override
    public void unregisterObserver(Observer o){
        if(observers.contains(o)){
            observers.remove(o);
        }
    }
    @Override
    public void notifyObserver(){
        for(int i=0;i<observers.size();i++){
            observers.get(i).update();
        }
    }
    
}

观察者抽象接口

public interface Observer{
    void update();
}

观察者具体的实现

public class ConcreteObserver implements Observer{
    private Subject subject;
    public ConcreteObserver(Subject subject){
        this.subject = subject;
        this.subject.registerObserver(this);
    }
    @Override
    public void update(){
        // 具体的实现
    }
}

到此关于观察者和被观察者都已经写完了,这仅仅是最简单的写法,当然 update() 方法里面可以传递参数,具体的实现让被观察者来实现。这就具体情况具体写了。

下面写 main 方法来测试

public static void main(String[] args){
    // 创建被观察者
    Subject subject = new ConcreteSubject();
    // 创建观察者
    Observer one = new ConcreteObserver(subject);
    // 创建观察者
    Observer two = new ConcreteObserver(subject);
    // 创建观察者
    Observer three = new ConcreteObserver(subject);
    // 状态发生改变,通知观察者
    subject.notifyObserver();
}

实际运用实例

在 Java 中实现观察模式,需要借助系统 API 提供的类,在包 java.util 中的 Observer 和 Observable 。被观察者需要继承 Observable 类,观察者需要实现接口 Observer 并且实现其中的 update(Observable o,Object arg) 方法

其实虽然系统提供了 Observer 和 Observable ,通过看源码你会发现它们的实现和我们上面写的例子是很像的,没什么难点。Observable 类里面主要就是有存放 Observer 的集合和方法 addObserver(Observer o) 、deleteObserver(Observer o) 、notifyObservers(Object arg) 观察者接口 Observer 内部主要有方法update(Observable o,Object arg)

简单运用

房子的价格和我们的生活息息相关,人们都很关注房价,假设房子是被观察者,人们是观察者,当房价发生变化的时候就会通知人们,这就就是一个观察者模式,用 Java 代码体现,下面列出核心代码,具体代码见链接

被观察者

public class House extends Observable {
    private float house_price;

    public House(float house_price) {
        this.house_price = house_price;
    }

    public void updatePrice(float house_price) {
        this.house_price = house_price;
        // 设置变化点
        setChanged();
        // 通知观察者
        notifyObservers(house_price);
    }

    @NonNull
    @Override
    public String toString() {
        return "当前房子价格:" + house_price;
    }
}

观察者

public class People implements Observer {
    private String name;

    public People(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        Float price = (Float) arg;
        Log.e("update", "我是观察者" + name + "房子价格变了:" + price);
        if (price > 20000) {
            Log.e("update:", "太TM贵啦,不关注了!");
            o.deleteObserver(this);
        }
    }
}

验证:

public class DesignModeActivity extends BaseActivity {

    @BindView(R.id.bt_change)
    Button btChange;
    @BindView(R.id.et_price)
    EditText etPrice;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_observer);
        ButterKnife.bind(this);
        init();
    }

    @Override
    public void initView() {
        super.initView();
        // 房子 属于被观察者
        House house = new House(10000);
        // 创建观察者
        People people = new People("张三");
        People people1 = new People("李四");
        People people2 = new People("王五");
        // 注册观察者
        house.addObserver(people);
        house.addObserver(people1);
        house.addObserver(people2);

        btChange.setOnClickListener(v -> {
            float price = Float.parseFloat(etPrice.getText().toString());
            house.updatePrice(price);
        });

    }
}

显示结果:

观察者模式详解_观察者模式属于什么模式

重点: 需要将观察者一一添加到被观察者中,当被观察者对象内容发生变化的时候调用 setChanged() 和 notifyObservers() 来通知观察者

Android 中的观察者

在 Android 中经常用到观察者模式,最常用的就是设计点击监听事件(这是一种特殊的观察者模式,观察者只有一个)、ContentObserver、android.database.Observable 等;还有组件通讯库RxJava、RxAndroid 、EventBus 等。这里拿 Adapter 中的观察者模式来举例:

先来看一下 BaseAdapter 的部分源码

public abstract class BaseAdapter implements ListAdapter,SpinnerAdapter{
    // 数据是否变化的被观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
    public boolean hasStablelds(){
        return false;
    }
    public void registerDataSetObserver(DataSetObserver observer){
        // 其实就是 mDataSetObservable 内部有一个 List 用来保存观察者
        mDataSetObservable.registerObserver(observer);
    }
    public void unregisterDataSetObserver(DataSetObserver observer){
        mDataSetObservable.unregisterObserver(observer);
    }
    // 当数据集合发生变化的时候,通知所有的观察者
    public void notifyDataSetChanged(){
        mDataSetObservable.notifyChanged();
    }
    
}

从源码中可以很容易的看到,这里有 被观察者对象 mDataSetObservable 当数据发生变化的时候,我们就调用 notifyDataSetChanged() 方法,这个时候 被观察者对象 就会通知观察者作出反应。就是这么一个过程!

整个过程我们都了解了,下面就是进一步看看代码是怎么实现的:看看 mDataSetObservable.notifyChanged() 做了什么

public class DataSetObservable extends Observable<DataSetObserver>{
    // 这个方法就是挨个通知观察者,其中储存观察者的 List 在 Observable 中,包括 registerObserver 和 unregisterObserver 方法
    public void notifyChanged(){
        synchronized(mObservers){
            for(int i = mObservers.size-1;i>=0;i--){
                mObservers.get(i).onChanged();
            }
        }
    }
}

这里的 mObservers 就是观察者的集合,这些观察者是在 ListView 通过 setAdapter() 设置 Adapter 时产生的:

@Override
public void setAdapter(ListAdapter adapter){
    if(mAdapter!= null && mDataSetObserver != null){
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    // .... 省略代码
    super.setAdapter(adapter);
    
    if(mAdapter != null){
        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
        mOldItemCount = mItemCount;
        mItemCount = mAdapter.getCount();
        checkFocus();
        //创建数据观察者
        mDataSetObserver = new AdapterDataSetObserver();
        //注册观察者
        mAdapter.registerDataSetObserver(mDataSetObserver);
        //... 省略代码
    }
       
}

再来看看观察者 AdapterDataSetObserver 的部分关键代码

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver{
    // 这个方法就是观察者在收到通知后,调用的方法
    @Override
    public void onChanged(){
        super.onChanged();
        if(mFastScroller != null){
            mFastScroller.onSectionsChanged();
        }
    }
    @Override
    public void onInvalidated(){
        super.onInvalidated();
        if(mFastScroller != null){
            mFastScroller.onSectionsChanged();
        }
    }
}

上面代码的主要实现部分都在它的父类,再来看看 AdapterDataSetObserver 的父类 AdapterView 的 AdapterDataSetObserver :

class AdapterDataSetObserver extends DataSetObserver{
    private Parcelable mInstanceState = null;
    @Override
    public void onChanged(){
        mDataChanged = true;
        mOldItemCount = mItemCount;
        mItemCount = getAdapter().getCount();
        if(AdapterView.this.getAdapter().hasStableIds()&& mInstanceState != null && mOldItemCount==0 && mItemCount > 0){
            AdapterView.this.onRestoreInstanceState(mInstanceState);
            mInstanceState = null;
        }else{
            rememberSyncState();
        }
        checkFocus();
        // 重新布局
        requestLayout();
    }
    // .... 省略代码
    
    public void clearSavedState(){
        mInstanceState = null;
    }
}

在源码中可以看到在 onChanged() 方法中调用了 requestLayout() 方法来重新进行布局。到此 BaseAdapter 的观察模式就缕清楚了。

观察者模式的使用场景和优缺点

使用场景

  • 事件多级触发场景
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制

优点

解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响到另一边的变换

缺点

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者,多个观察者,开发、调试等内容会比较复杂,而且在 Java 中消息的通知一般是顺序执行的,那么一个观察者卡顿,就会影响整体的执行效率,在这种情况下,一般会采用异步实现。

总结:

其实无论程序整体多复杂,观察者模式是永远不会变的,首先有一个被观察者,被观察者对象中持有观察者,当被观察者发生变化的时候,调用被观察者的方法通知观察者(这个方法内通常会是 观察者调用自己对应的方法)。

至于观察者什么时候被添加到被观察者中,观察者收到信号后如果处理,这就是具体情况具体处理了。

参考内容 blog.csdn.net/itachi85/ar…> www.jianshu.com/p/9ee823cd9… www.2cto.com/kf/201310/2…

观察者模式详解_观察者模式属于什么模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13706.html

赞 (0)

相关推荐

  • 32位cpu中的32指的是什么_cpu高和内存有关系吗

    32位cpu中的32指的是什么_cpu高和内存有关系吗一、32位cpu为什么只支持最大4G内存? 32位最大支持4G内存,到底是怎么算出来的? 去网上搜索答案,大部分都会甩给一个公式 2^32 = 4GB 我当时看到这个公式的时候,没太在意,后来我自己换

    2023-08-15
    130
  • 使用Python中的 Break 关键字在 For 循环中提前退出

    使用Python中的 Break 关键字在 For 循环中提前退出在Python中,break关键字常用于循环语句中的执行结构。使用break对于遇到指定情况的代码块将会提前退出循环,从而节省计算并优化程序执行速度。

    2024-01-08
    114
  • 使用Python对List进行排序的方法

    使用Python对List进行排序的方法排序是编程中非常基础的操作之一,它可以帮助我们更好地组织和处理数据。在Python中,有多种方法可以对List进行排序。本文将从多个方面对使用Python对List进行排序的方法进行详细阐述。

    2024-05-20
    73
  • 链接服务器读取Mysql-「终于解决」

    链接服务器读取Mysql-「终于解决」可以毫不夸张的说:“网上所有搜索出来的答案,都没有解决我的问题”,我是采用以下的方式处理此异常,借此宝地mark一下 今天使用链接服务器查询Mysql数据库时,出现以下问题: 消息 7347,级别 1

    2023-05-01
    145
  • Python Mapping Essentials

    Python Mapping Essentials在现代技术中,地图信息是至关重要的。在生产、运输、安全等领域,地图是一个非常重要的工具。Python开发者可以使用各种库来推动地图应用的开发,例如:基于Google Maps、OpenStreetMap等地图数据源的开发。在本文中,我们将介绍使用Python开发地图应用所需的必要技能和知识方面,并将涉及一些有关地图数据采集、分析和可视化的功能。

    2024-05-07
    72
  • 未来,软+硬+AI三位一体化自治数据库

    未来,软+硬+AI三位一体化自治数据库在2022世界人工智能大会(WAIC)上,腾讯云数据库技术负责人程彬为大家分享了数据库与 AI 相结合背后的故事。在专场《当数据库遇上 AI 》中,程彬基于腾讯云数据库在 AI 智能化的探索与实践,剖

    2023-06-04
    152
  • unity3d脚本(unity3d嵌入网页)

    unity3d脚本(unity3d嵌入网页)

    2023-09-21
    150
  • sql 如何使用内联结,外联结和交叉联结的区别_sql内外连接

    sql 如何使用内联结,外联结和交叉联结的区别_sql内外连接本文介绍 SQL 如何使用内联结(INNER JOIN)、外联结(OUTER JOIN)和交叉联结(CROSS JOIN)。简单来说,就是将其他表中的列添加过来,进行“添加列”的运算。 本文重点 联结

    2023-05-22
    124

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注