`

扩展性改造--观察者模式

阅读更多

目录

 

一、观察者模式介绍

二、实例讲解

三、在Srping 中整合“观察者模式”

四、扩展性体现

五、Java自带观察者模式支持

 

引言

 

在java项目开发中,经常会把一些重要的数据放到数据库里,但如果这些数据在程序中会经常被使用到,就会频繁的查询数据库,导致数据库压力增加。常见的做法是把这些数据放到缓存里,比如redis等全局共享缓存,这样每次使用的使用时候不用查询数据库。

 

但如果使用redis作为缓存,每次读取都有网络开销。如果数据量不大的情况下,可以在程序启动时把这部分数据加载到jvm内存中(比如,public static的变量),每次使用这些数据的时候,直接从本地jvm内存中获取 性能方面势必会好很多,而且减少了外部依赖,系统稳定性方面也会好一些。但也有一个缺点:现在的程序都是多实例部署(多个jvm实例),每个jvm实例内存中都有一份数据,如果数据有修改,要同步更新每个jvm内存中数据 会有些困难。

 

常见的解决方案是引入配置管理系统(类似淘宝的diamond),多个jvm实例会向“配置管理系统”中的某个配置文件进行注册,当这些配置文件发送变化时,就会通知各个jvm实例拉取最新的配置。其实这个场景就是一个“观察者模式”的放大化使用,可以把“配置管理系统”中的某个配置文件看做是“主题”,多个jvm实例看做是“观察者”。当主题发生变化时,会通知观察者拉取最新的内容。



 

 

一、观察者介绍

 

定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。其中“一”的一方称为“主题对象”,“多”的一方称为“观察者对象”。采用此模式的优点:

1、“主题对象”和“观察者对象”之间相互隔离,彼此内部业务有修改,互不干扰。

2、“观察者对象”之间相互隔离,新增或删除“观察者对象”,不会影响其他“观察者对象”,具备良好的扩展性。

 

观察者模式中有4类角色:“抽象的主题”(接口 Subject)、“抽象的观察者”(接口 Observer)、“具体的主题”(实现 ConcreteSubject)、“具体的观察者”(实现 ConcreteObserver,可以有多个)。类图关系如下:



 

                             (来自互联网)

 

“推送型”和“拉取型

 

根据数据的同步方式,“观察者模式”又分为“推送型”和“拉取型”:

1、“推送型”:观察者的update方法参数中包含所有的业务数据,“具体的观察者”根据自己的业务获取自己需要的数据。优点是数据隔离性好,不会对主题对象造成破坏;缺点是 如果参数过多看起来会比较臃肿,有些观察者不需要的数据也会被推送。

 

2、“拉取型” :在“具体的主题对象”实现中提供一些获取数据的public方法,在update方法中直接调用这些方法获取自己需要的数据。优点是 自己需要什么数据自己去拿,缺点是 如果“具体的主题对象”实现设计不当,容易暴露一些私有的数据和方法。

 

当然也不是绝对的只有这两种,有些主题里的数据量很少,有可能只需要一个状态变化来触发“具体的观察者”对象的update方法做一些业务操作即可,不需要获取主题中的数据。下面实例讲解中的案例,就属于这种情况。

 

二、实例讲解

 

“观察者模式”最简单明了的例子是《Head First》设计模式一书中“气象观察站”的例子,可以网上搜索了解下,这里不细讲。实际项目中,很难遇到与此场景完全匹配的例子,但整体思路是一致的,根据实际情况调整即可。

 

这里引用最近项目中的一个实际例子进行讲解,本实例完整代码详见github:https://github.com/gantianxing/observer-test.git

 

回到文章开头的场景,最近一个项目中需要把一些常用的数据放到jvm内存,引入配置管理系统进行数据同步。由于配置数据项较多,本项目中没有把配置数据直接以配置文件的形式放到“配置管理系统”,而是把具体的数据放到数据库,再把每项数据是否修改做出开关配置放到“配置管理系统”。数据同步流程为:

1、程序启动时,从数据库读取数据放到jvm内存。

2、当数据库中某条数据发生变化时:“数据管理后台”发布新数据到数据库,同时修改“配置管理系统”中的配置开关。这里有一个“配置开关”列表,对应数据库中的多个数据项。

3、各个jvm实例获取到变化的配置开关,触发再次读取数据库中的新数据 同步到jvm内存。

这里的“配置开关”列表即为“具体的主题对象”,“多个数据项”对应的多个同步类即为:“具体的观察者”。上述流程关系如下:

 


本次讲解忽略“数据管理后台”相关操作,主要关注 当开关列表发生变化时,触发对应的“同步类”从数据库拉取最新数据。该部分流程,采用“观察者模式”实现:

 

1、抽象的主题(ConfigSubject):

 

 

public interface ConfigSubject {
    void registerObserver(String key, ConfigRloadService o);//注册观察者
    void removeObserver(String key);//删除观察者
    void notifyObervers(String conf);//通知观察者
}

 

 

2、抽象的观察者(ConfigRloadService):

 

public interface ConfigRloadService {
    void reload();
}
 

 

 

3、具体的主题(ConfigSubjectImpl):

 

@Component("subject")
public class ConfigSubjectImpl implements ConfigSubject {

    //观察者列表
    private Map<String,ConfigRloadService> observers = new HashMap();

    @Override
    public void registerObserver(String key,ConfigRloadService o) {
        observers.put(key, o);
    }

    @Override
    public void removeObserver(String key) {
        observers.remove(key);
    }

    /**
     * 通知对应的观察者
     * @param conf
     */
    @Override
    public void notifyObervers(String conf) {

        if(conf!=null && conf.length()>0){
            String [] configAray=conf.split("\\r\\n"); //回车换行做为配置分割,一行一个配置项
            for(String one:configAray){
                String [] oneArray = one.split("=");

                if(oneArray.length !=2){
                    continue;
                }

                String key = oneArray[0];
                String value = oneArray[1];

                int iv_new = Integer.parseInt(value);
                int iv_old = SwitchEnum.valueOf(key).getValue();

                if(iv_new != iv_old){//如果配置开关不相等,说明配置内容已经更改,需要重新reload数据
                    ConfigRloadService service = observers.get(key);
                    if(service != null){
                        service.reload();
                    }
                    SwitchEnum.valueOf(key).setValue(iv_new);
                }
            }
        }
    }
}

 

 

4、具体的观察者(两个:ActPcRloadServiceImpl、ActMobRloadServiceImpl)

@Component
public class ActPcRloadServiceImpl implements ConfigRloadService {
 
    @Resource
    private ConfigSubject subject;
 
    public ActPcRloadServiceImpl(ConfigSubject subject){
 
        this.subject = subject;
        subject.registerObserver(SwitchEnum.actPcConf.name(),this);
    }
 
    @Override
    public void reload() {
        //查询数据库加载最新的配置内容到jvm内存,代码逻辑省略
        System.out.println("pc版活动配置reload");
    }
 
}

 

 

@Component
public class ActMobRloadServiceImpl implements ConfigRloadService {
 
    @Resource
    private ConfigSubject subject;
 
    public ActMobRloadServiceImpl(ConfigSubject subject){
 
        this.subject = subject;
        subject.registerObserver(SwitchEnum.actMobConf.name(),this);
    }
 
    @Override
    public void reload() {
 
        //查询数据库加载最新的配置内容到jvm内存,代码逻辑省略
        System.out.println("移动端活动配置reload");
    }
}
 

 

辅助常量枚举类,对应多个配置开关,这里只有两个,分别对应上述两个“具体的观察者”:

public enum SwitchEnum {
 
    actMobConf(0),
    actPcConf(0);
 
    private int value;
 
    public int getValue() {
        return value;
    }
 
    public void setValue(int value) {
        this.value = value;
    }
 
    SwitchEnum(int value){
        this.value = value;
    }
}
 

 

测试类:

public class Main {
 
    public static void main(String[] args) {
        String conf = "actMobConf=1\r\nactPcConf=0";//模拟开关配置发生改变
 
        //step1 初始化主题和观察者,在spring中可以用spring ioc自动注入代替
        ConfigSubject subject = new ConfigSubjectImpl();//创建主题
        ConfigRloadService actMobRloadServiceImpl = new ActMobRloadServiceImpl(subject);//创建观察者
        ConfigRloadService actPcRloadServiceImpl = new ActPcRloadServiceImpl(subject);//创建观察者
 
        //step2 模拟开关配置改变,触发观察者reload方法
        subject.notifyObervers(conf);
 
    }
}

其中String conf = "actMobConf=1\r\nactPcConf=0"; 这里有两个配置开关,分别对应SwitchEnum中的两个配置开关,修改String conf中配置值与SwitchEnum中默认的默认值不同,即可触发指定的“同步类”从数据库拉取新数据。这里把actMobConf改为了1,按逻辑会触发ActMobRloadServiceImpl的reload方法执行。运行上述main方法,查看结果:

 

移动端活动配置reload

 

说明测试结果与预期相符。

 

三、在Srping 中整合“观察者模式

 

上一节使用的main方法来实例化bean,在实战中我们一般都是使用的Spring ioc容器,使用起来更方便,直接添加@Component注解即可。这里就不细讲,可以直在tomcat中运行github: https://github.com/gantianxing/observer-test.git中的代码,启动后访问http://localhost

,修改配置项 观察控制台日志看效果。



 

 

观察控制台日志:



 

 

四、扩展性体现

 

假设现在业务新增一项数据 需要同步到jvm内存,此时只需要三步操作:

1、新增一个同步类XXXRloadServiceImpl(实现ConfigRloadService接口),实现自己的数据加载方法reload。

2、在枚举类SwitchEnum中新增一个配置项xxxPcConf(0)。

3、在配置管理系统的 配置开关列表中,新增一个开关xxxPcConf=1,即可实现数据同步。

可见整个扩展过程,对原有逻辑没有任何影响。

 

五、Java自带观察者模式支持

 

由于观察者模式是一个常见的设计模式,Java api提供的两个工具类对“观察者模式”进行支持,java.util包中的Observable类 和Observer接口:

Observable类对应“抽象的主题”,只是这里不是接口,“具体的主题”需要继承该类。

Observer接口对应“抽象的观察者”,“具体的观察者”实现该接口即可。

 

有些简单的场景可以直接使用,但有一定局限,比如:

1、Observable主题类中的“观察者”列表,是用的Vector存储。上述示例中需要用Map就无能为力。

private Vector<Observer> obs = new Vector();

 

2、notifyObservers方法,遍历所有的“观察者”,执行其update方法。上述示例中如果需要过滤部分“观察者”执行其update方法,也无能为力。

 

其实观察者模式实现起来也比较简单,根据自己的业务自己实现也不难,java自带的可以作为参考即可,尤其是的Observable类的设计思想。

 

转载请注明出处:

http://moon-walker.iteye.com/blog/2395614

  • 大小: 21.8 KB
  • 大小: 92.7 KB
  • 大小: 61.2 KB
  • 大小: 7.3 KB
  • 大小: 28.3 KB
0
0
分享到:
评论

相关推荐

    《设计模式--基于C#的工程化实现及扩展》.(王翔)_《0528》.rar

    行为型模式则关注对象之间的交互和职责分配,如策略模式(Strategy)、观察者模式(Observer)和责任链模式(Chain of Responsibility)等,它们有助于提高代码的灵活性和可扩展性。 在C#中,设计模式的实现往往...

    学习笔记-深入浅出设计模式

    在《深入浅出设计模式》的学习笔记中,我们可以看到几种关键的设计模式及其应用,包括策略模式、观察者模式、装饰者模式、工厂模式和抽象工厂模式。 1. **策略模式**: - 策略模式的核心在于将算法族封装在独立的...

    Javascript设计模式之观察者模式(推荐)

    在代码注释中,提到将observer改造成构造函数的方式,即让village2也采用同样的发布-订阅机制,这表示将观察者模式进行泛化,使它可以适用于多个不同的“村子”对象。这种改造能够使系统更加灵活,扩展性更好。 ...

    设计模式 基于C#的工程化实现及扩展 王翔 pdf

    接着,书中会逐一解析创建型模式(如工厂方法、抽象工厂、单例等)、结构型模式(如适配器、装饰器、代理等)和行为型模式(如策略、观察者、责任链等)。每种模式都会结合C#代码实例进行详细阐述,帮助读者更好地...

    基于设计模式的绘板程序

    观察者模式允许多个对象监听这些事件并做出响应,提高了代码的灵活性和可扩展性。 接着,"策略模式"可能用于处理不同的绘图工具。例如,画笔、橡皮擦、选择工具等都可以看作是不同的策略,根据用户的选择动态地改变...

    <设计模式>各个模式简单模式源代码

    11. 观察者模式:定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式广泛应用于事件驱动编程和发布订阅系统。 12. 状态模式:允许对象在其内部状态...

    设计模式课件配套程序

    观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在C++中,观察者模式常用于事件驱动编程,如GUI界面中的事件处理。...

    《设计模式》中文版_几种模式的详细讲解与使用

    3. **观察者模式(Observer)**:定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 4. **命令模式(Command)**:将一个请求封装为一个对象,从而使你可用...

    《设计模式》课件1代码

    6. **观察者模式**(发布/订阅):定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 7. **装饰器模式**:动态地给一个对象添加一些额外的职责,提供了一...

    重构与模式 中文

    例如,在现有代码中引入观察者模式时,可能需要先进行一系列的重构操作,比如提取接口、调整类结构等,才能顺利地将模式融入现有设计中。 ### 实践中的应用 #### 使用模式改善现有设计 《重构与模式》一书强调了...

    设计模式rar

    5. **观察者模式**:定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在事件驱动编程中非常常见。 6. **装饰器模式**:动态地给一个对象添加一些...

    BAT面试题大全进大厂必看358页精品,2022年最新资源,助您斩获offer!

    - Dubbo在实现中广泛应用了工厂模式、代理模式、观察者模式等设计模式。 15. **Dubbo的运维管理** - 支持telnet命令进行服务管理和诊断 - 支持服务降级,当服务不可用时,可以降级为本地缓存或返回默认值 - ...

    学习资料合集,你可以的~

    - **JS常用设计模式**:包括工厂模式、单例模式、观察者模式等。 - **应用场景**:改善代码的可读性、可维护性和扩展性。 ### 四、疑难问题解决方案 #### 常见问题分析 - **性能优化**:针对响应时间过长、内存...

    MQ测试器-MQ分析

    - **性能评估**:通过大量并发发送和接收测试,评估MQ的吞吐量、延迟和可扩展性。 - **故障模拟**:模拟网络故障、服务器故障等异常情况,测试MQ的容错和恢复机制。 - **日志分析**:提供日志查看和分析功能,...

    C++编程思想 改造你的思想

    6. **设计模式**:设计模式是软件设计中的一些常见问题的解决方案,如单例模式、工厂模式、观察者模式等。理解和应用设计模式能提高代码质量,使程序更易于维护和扩展。 7. **程序设计原则**:书中可能会介绍一些...

    covid-tracker-改造

    ViewModel帮助在Activity或Fragment重建时保持数据,而LiveData则提供了一个观察者模式,使得UI可以实时响应后台数据的变化。 3. **Retrofit库**:Retrofit是由Square公司开发的一个HTTP客户端库,它允许开发者以...

    2021抖音美好居住观察报告-50页

    报告指出,随着社会的发展和消费观念的升级,人们对美好居住的定义已经从单纯的物质满足扩展到精神享受。 报告中提到,中国家庭户均人口规模在减少,家庭模式正向小型化转变,这直接影响了人们对居住空间的需求。...

    智慧工厂可视化方案.docx

    - 可持续发展:考虑未来的业务扩展需求,确保系统的可扩展性。 - 安全可靠:保障数据安全和个人隐私。 - **设计依据**: - 国家相关政策法规,如《中国制造2025》等。 - 行业标准和技术规范,确保系统符合行业...

    Software Architecture Design Pattern In Java

    4. **观察者模式(Observer Pattern)**: - 定义:定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 - 应用场景:适用于实时更新显示数据的应用程序,...

Global site tag (gtag.js) - Google Analytics