`

观察者模式(发布-订阅)

 
阅读更多

由于网站带有弱sns功能,因此需要设计关注和被关注的消息或是动作通知,那么将这个需求抽象出来的时候就会发现正好符合java中发布订阅模式。

一、概述
Java 的设计模式很多,观察者模式被称为是模式中的皇后,而且Java jdk也对它做了实现,可见该设计模式的重要位置。在图形化设计的软件中,为了实现视图和事件处理的分离,大多都采用了Observer模式,比如 Java的Swing,Flex的ActionScript等。在现实的应用系统中也有好多应用,比如像当当网、京东商城一类的电子商务网站,如果你对某 件商品比较关注,可以放到收藏架,那么当该商品降价时,系统给您发送站内消息、手机短信、邮件。这就是观察者模式的一个典型应用,商品是被观察者,关注该商品的客户 就是观察者。

GoF说道:Observer模式的意图是“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”。参见下图:




可以看出来,观察者模式,是一种一对多的关系,即多个观察者监听一个主题。

 

二、示例代码

商品价格打折后,所有关注、收藏该商品的用户都收到相关的信息提醒。

角色:

1)商品:被观察者;

2)用户:观察者

 

1.商品(发布者)

 

    import java.util.ArrayList;  
    import java.util.Iterator;  
    import java.util.List;  
      
    /** 
     * 商品-发布者 
     * @author Administrator 
     * 
     */  
    public class Product {  
        private String name;  
        private double price;  
        private List<Observer> focusUsers;//观察者集合  
          
        /** 
         * 价格折扣 
         * @param off 
         */  
        public synchronized void payOff(double off){  
            this.price = getPrice() * (1 - off);          
            StringBuffer msg = null;  
              
            if(focusUsers != null && !focusUsers.isEmpty()){  
                Iterator it = focusUsers.iterator();  
                while(it.hasNext()){  
                    Observer user = (Observer)it.next();  
                      
                    String msgPart = ", "+ this.getName() +"的价格 "+ this.getPrice() +", 价格折扣 "+ off * 100 +"%!";  
                    msg = new StringBuffer();  
                    msg.append("~~~~ 您好 "+ user.getName());  
                    msg.append(msgPart);  
                      
                    user.notify(msg.toString());//发送提醒  
                }  
            }  
        }  
          
        /** 
         * 添加关注用户 
         * @param user 
         */  
        public void addFocusUsers(User user) {  
            this.getFocusUsers().add(user);  
        }  
        /** 
         * 删除关注用户 
         * @param user 
         */  
        public void delFocusUser(User user) {  
            this.getFocusUsers().remove(user);  
        }  
          
          
        public Product(){  
            focusUsers = new ArrayList<Observer>();  
        }  
          
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public double getPrice() {  
            return price;  
        }  
        public void setPrice(double price) {  
            this.price = price;  
        }  
          
          
        public List<Observer> getFocusUsers() {  
            return focusUsers;  
        }  
        public void setFocusUsers(List<Observer> focusUsers) {  
            this.focusUsers = focusUsers;  
        }  
          
    }  

 

 

2.观察者(订阅者)接口

 

    /** 
     * 观察者(订阅者)接口 
     * @author Administrator 
     * 
     */  
    public interface Observer {  
          
        public void notify(String msg);  
          
        public String getName();  
          
    }  

 

 

3.观察者(订阅者)

 

import java.util.HashSet;  
import java.util.Set;  
  
/** 
 * 观察者(订阅者) 
 * @author Administrator 
 * 
 */  
public class User implements Observer {  
    private String name;  
    private Set<Product> focusPdts;  
      
    /** 
     * 通知方法 
     */  
    public void notify(String msg){  
        System.out.println(msg);  
    }  
      
    public User(){  
        focusPdts = new HashSet<Product>();  
    }  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public Set<Product> getFocusPdts() {  
        return focusPdts;  
    }  
    public void setFocusPdts(Set<Product> focusPdts) {  
        this.focusPdts = focusPdts;  
    }     
      
} 

 

 

4.client端调用

 

    public class client {  
      
        /** 
         * @param args 
         */  
        public static void main(String[] args) {  
            //产品  
            Product mobile = new Product();  
            mobile.setName("SAMSUNG手机");  
            mobile.setPrice(2000);  
              
            Product book = new Product();  
            book.setName("JAVA设计模式");  
            book.setPrice(80);  
              
            //用户  
            User user1 = new User();  
            user1.setName("张三");  
            user1.getFocusPdts().add(mobile);//关注某一款三星手机  
            //user1.getFocusPdts().add(book);//关注JAVA设计模式  
              
            User user2 = new User();  
            user2.setName("李四");  
            user2.getFocusPdts().add(mobile);//关注某一款三星手机  
            user2.getFocusPdts().add(book);//关注JAVA设计模式  
              
            //建立商品和订阅者关联  
            mobile.getFocusUsers().add(user1);  
            book.getFocusUsers().add(user1);  
            book.getFocusUsers().add(user2);  
              
            //产品打折,发送站内信提醒  
            mobile.payOff(0.1);  
            book.payOff(0.2);  
        }  
      
    }  

 

 

三、功能设计

常用的处理方式:

将数据库作为数据存储的介质,消息提醒数据保存在数据库表中,采用定时任务的方式来汇总和发送。具体流程:

1.存储用户-关注关联数据

将用户和所关注的数据存储到一张“用户-关注商品关联表”;

 

2.执行汇总任务

商品打折时,触发汇总任务,遍历“用户-关注商品“关联表,将符合发送条件的记录汇总到”提醒消息表“;数据量巨大的情况下,可采用在“用户-关注商品关联表”冗余字段的方式,不再创建”提醒消息表“减小数据量。

 

3.发送折扣提醒消息

遍历”提醒消息表“并发送,发送完成后,将记录标示为已发送。

 

四、设计分析

如果系统的用户、商品数量都很大,这种情况下如何设计功能更合理呢,个人认为有几点需要关注:

1)响应及时性

2)数据的持久性

3)web层压力

4)数据库层压力

5)系统资源的消耗

 

内存方式:   采用观察者模式,将关注用户保存在商品对象中,也就是存储在java 堆中。

数据库方式:采用传统关系型数据,例如mysql等。

 


项目

内存

数据库

分析

响应及时性

较好

较差

内存操作比起数据库操作肯定性能上好很多

数据的持久性

较差

较好

如果出现宕机等故障,内存数据会被清空,导致整个功能异常。

所以说数据仅保存在内存有缺陷,将内存数据持久化到数据库中做备份是较完备的方案,具体实现暂不讨论。

web层压力

较大

中等

由于用户和商品数量巨大,商品-内存关联数据保存在内存中对系统内存消耗较大,假如1000W用户,每个用户关注10个商品的话,每条记录100Byte,那么大致占用10G左右,直觉上对内存占用较大,会影响整个系统的表现。

数据库层压力

较大

用户-商品关注关联表,约1亿条数据。发送提醒消息和更改发送标示需要1次读操作、1次写操作,对数据的存储和数据库的压力都是一个挑战。另外,实现上肯定要采用缩小每次读写操作的数据集的方式。

系统资源消耗

中等

中等

内存方式,对系统内存占用较大,但对其他系统资源消耗不大。数据库方式,对系统的数据库层有较大的压力。

 

通过以上分析,发现两种方式都有比较大的问题,那是否可以采用key-value型内存软件加持久数据到数据库中的方式来实现呢?
1)key-value型内存操作比直接数据库操作(磁盘io)操作性能上好很多;
2)将内存数据保存1份到数据库应对内存失效问题,采用异步持久化方式可减小对系统整体资源的消耗。
 

 

  • 大小: 9.2 KB
1
2
分享到:
评论
8 楼 orangebook 2015-09-14  
对于初学者来说,这样编写可能会误导,理解更烦锁。
7 楼 charles751 2013-08-12  
zld406504302 写道
想法不错,不过设计上个人感觉有些问题。
Product 与 user 形成了多对多的依赖,这样user 信息就会通过Product 彼此暴露


你认为比较好的方式是什么呢?
6 楼 zld406504302 2013-08-12  
想法不错,不过设计上个人感觉有些问题。
Product 与 user 形成了多对多的依赖,这样user 信息就会通过Product 彼此暴露
5 楼 charles751 2013-08-11  
zczh3 写道
zczh3 写道
user1.getFocusPdts().add(mobile);//关注某一款三星手机 

这种接口,只能称为产品集合中添加产品

个人觉得,真正的关注只应该有两个对象,对象A关注了B
接口应该设计成A.focusOn(B)

按照这样设计后,

        //建立商品和订阅者关联   
        mobile.getFocusUsers().add(user1);   
        book.getFocusUsers().add(user1);   
        book.getFocusUsers().add(user2);  


这些语句不应该直接呈现给客户端,而是将这个语句纳入focusOn语句内,否则难道让客户端理解为:产品还要关注用户吗?

理解不一定正确,请斧正哈



补充一下,比如我在关注博主,难道还要求博主再将我加一下关注列表



对你比较纠结的解释:
这句是为了让用户方便看到他所关注的商品列表,和观察者模式无关。

“比如我在关注博主,难道还要求博主再将我加一下关注列表”
貌似看上去没问题,但你没有真正理解“为什么需要用户对象中还要保存关注产品”。

客户端是应用程序的调用,你认为是什么样的客户端呢?

4 楼 zczh3 2013-08-09  
zczh3 写道
user1.getFocusPdts().add(mobile);//关注某一款三星手机 

这种接口,只能称为产品集合中添加产品

个人觉得,真正的关注只应该有两个对象,对象A关注了B
接口应该设计成A.focusOn(B)

按照这样设计后,

        //建立商品和订阅者关联   
        mobile.getFocusUsers().add(user1);   
        book.getFocusUsers().add(user1);   
        book.getFocusUsers().add(user2);  


这些语句不应该直接呈现给客户端,而是将这个语句纳入focusOn语句内,否则难道让客户端理解为:产品还要关注用户吗?

理解不一定正确,请斧正哈



补充一下,比如我在关注博主,难道还要求博主再将我加一下关注列表
3 楼 zczh3 2013-08-09  
user1.getFocusPdts().add(mobile);//关注某一款三星手机 

这种接口,只能称为产品集合中添加产品

个人觉得,真正的关注只应该有两个对象,对象A关注了B
接口应该设计成A.focusOn(B)

按照这样设计后,

        //建立商品和订阅者关联   
        mobile.getFocusUsers().add(user1);   
        book.getFocusUsers().add(user1);   
        book.getFocusUsers().add(user2);  


这些语句不应该直接呈现给客户端,而是将这个语句纳入focusOn语句内,否则难道让客户端理解为:产品还要关注用户吗?

理解不一定正确,请斧正哈
2 楼 charles751 2013-08-08  
duxitao 写道
博主您好,请问这句有什么作用  
    user1.getFocusPdts().add(mobile);//关注某一款三星手机   
没有这一句,商品打折是也可以通知到用户的


你说的没错。
这句是为了让用户方便看到他所关注的商品列表,和观察者模式无关。
1 楼 duxitao 2013-08-08  
博主您好,请问这句有什么作用  
    user1.getFocusPdts().add(mobile);//关注某一款三星手机   
没有这一句,商品打折是也可以通知到用户的

相关推荐

    发布-订阅者模式的一个小例子程序

    发布-订阅者模式,也称为观察者模式,是软件设计模式中的一种常用模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式广泛应用于事件驱动...

    JAVA 发布-订阅(观察者)模式

    在Java中,观察者模式主要通过Java的`java.util.Observable`和`java.util.Observer`接口来实现。 首先,我们要理解“发布-订阅”模式的基本概念。在这一模式中,我们称事件的发布者为Subject(主题),而对事件感...

    观察者模式(发布-订阅)(转载含实例)

    观察者模式,也被称为发布-订阅(Publish-Subscribe)模式,是软件设计模式中的行为模式之一,它在对象之间定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。...

    设计模式实现——观察者模式

    7. **与发布-订阅模式的关系**:观察者模式是发布-订阅模式的一种实现方式。在发布-订阅模式中,发布者发布事件,订阅者订阅感兴趣的事件,当事件发生时,订阅者会收到通知。两者的主要区别在于观察者模式更强调对象...

    设计模式-观察者模式-作业-observer.zip

    观察者模式(Observer Pattern)是软件设计模式中的行为模式之一,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于实现事件驱动或发布-...

    《深入浅出Node.js》:Node异步编程解决方案之事件发布-订阅模式.docx

    事件发布-订阅模式,又称为观察者模式,是异步编程中常用的一种设计模式。它将回调函数事件化,使得代码更易于理解和维护。在Node.js中,`events`模块提供了这一模式的实现,大多数核心模块如http、fs和stream都继承...

    设计模式--观察者模式java例子

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于事件驱动的系统或者...

    android学习日记-6 观察者模式与订阅者模式

    在Android开发中,观察者模式(Observer Pattern)和订阅者模式(Subscriber Pattern)是两种重要的设计模式,它们在实现事件驱动编程中起着关键作用。本文将深入探讨这两种模式的概念、工作原理以及在Android开发中...

    设计模式专题之(十八)观察者模式---设计模式观察者模式示例代码(python--c++)

    观察者模式是软件设计模式中的一种行为模式,它允许一个对象,称为"主题",维护一个依赖于它的对象列表,这些对象被称为"观察者"。当主题的状态发生改变时,它会通知所有观察者,使它们能够自动更新自己。这种模式在...

    18 发布-订阅模式:去 adadis 买鞋.pdf

    发布-订阅模式,又称观察者模式,是一种广泛应用于软件开发中的设计模式,尤其在JavaScript中。这个模式的主要目的是实现对象间的一对多依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知...

    java观察者模式观察者模式

    观察者模式是设计模式中的一种行为模式,它在Java编程中有着广泛的应用。该模式的主要目的是定义对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式也被...

    设计模式-观察者

    观察者模式的核心思想是发布-订阅模型,其中被观察的对象被称为主题(Subject),而关心主题状态变化的其他对象称为观察者(Observer)。在Java中,`java.util.Observable` 和 `java.util.Observer` 类为实现观察者...

    设计模式-观察者模式.ppt

    观察者模式是一种行为设计模式,它允许你定义一个订阅机制,可以在对象事件发生时通知多个“观察”该对象的其他对象。这个模式的核心在于建立一种一对多的关系,当一个对象的状态改变时,所有依赖于它的对象都会得到...

    Android设计模式--观察者模式DEMO

    Android设计模式--观察者模式DEMO 观察者模式是一个使用频率非常高的模式,他最常用的地方是GUI系统和订阅-发布系统。 该模式的最重要的作用就是解耦,使观察者和被观察者之间依赖尽可能小,甚至好无依赖。

    JavaScript设计模式之观察者模式(发布者-订阅者模式)

    在JavaScript中,观察者模式也被称作发布-订阅模式(Publish-Subscribe Pattern),是一种行为设计模式,它允许对象之间通过事件来通信。 观察者模式的核心概念包括观察者(Observer)和目标(Subject)。观察者是...

    设计模式-观察者模式(讲解及其实现代码)

    观察者模式,也被称为发布-订阅模式,是软件设计中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在分布式系统、事件驱动...

    游戏设计2-观察者模式的应用-攻击

    5. **性能优化**:为了提高性能,观察者模式还可以结合“发布-订阅”模型,其中主体发布一个消息到消息队列,而观察者则根据需要选择性地订阅并处理这些消息,避免了不必要的计算。 在提供的GameDemo文件中,我们...

    利用观察者模式(发布/订阅模式)制作一个“代替”广播的通知类

    观察者模式,也被称为发布/订阅模式,是软件设计模式中的一个重要概念,它在很多场景下用于实现对象之间的通信和事件处理。在这个模式中,一个主题(Subject)维护了一个观察者(Observer)列表,当主题的状态发生...

    设计模式系列博客--观察者模式

    观察者模式的核心思想是将数据与处理数据的逻辑分离,数据变化时通过接口通知订阅者,让订阅者能够及时响应并执行相应的操作。 策略模式(Strategy Pattern)则是另一种行为模式,它定义了一系列算法,并将每一个...

    java 设计模式 观察者模式 简单实例 包括测试test类

    观察者模式广泛应用于各种领域,例如GUI事件处理、订阅-发布系统、实时数据更新等。在GUI编程中,按钮点击、文本框输入等事件都可以通过观察者模式来处理。在订阅-发布系统中,用户订阅某个话题,当有新消息时,...

Global site tag (gtag.js) - Google Analytics