java设计模式--观察者模式(附代码详细讲解)

观察者模式(Observer Pattern)

一、相关概念

定义

当对象存在一对多关系时,则使用观察者模式。当一个对象被修改时,则自动通知依赖它的对象,但要考虑到易用和低耦合。例如:股票价格和股民

观察者模式属于行为性模式

别名:发布-订阅模式,后面‘订阅-发布模式’被优化多了一个处于观察者与被观察者之间的中转站,实现了解耦。

优点

  • 观察者与被观察者是抽象耦合
  • 建立了一套触发机制
  • 满足开闭原则,增加新的 观察者不需要修改原先代码

缺点

  • 如果一个被观察者对象具有多个直接或者间接的观察者 ,通知所有的观察者会耗费较多时间

  • 观察者与被观察者直接有循环依赖,会导致系统崩溃(死循环)

目标

解除系统耦合性,不用在维护对象的一致性中而使系统紧密耦合。

结构

1.抽象主题(Sbuject):

也叫抽象目标类,提供了一个用于保存观察者对象的聚集类。里面提供了对应的增加(addObserver)、删除(deleteObserver)和通知所有观察者对象(notifyObservers)的方法。

2.具体主题(Concrete Sbuject):

也叫具体目标类,它实现抽象目标中的通知方法,也当具体主题的内部发生变化时,通知所有注册过的观察者对象

3.抽象观察者(Observer):

它是一个抽象类,包含一个更新自己的抽象方法,当接到具体主题的更改通知时被调用

4.具体观察者(Concrete Observer):

它实现抽象观察者中的更新方法,当接到具体主题的更改通知时,给观察者对象做出相应的处理,如发送消息。

二、代码实现流程

1.建立观察者与被观察者直接的关系

/***
 * Web Scoket服务.
 * <li>观察者模式
 */
@Component
@ServerEndpoint(value = "/websocket/{principal}")
public class WebSocketEndpoint {

    /**
     * Web Socket连接建立成功的回调方法
     * @param principal 通过该字符串找到不同的主题
     * @param session 观察者的session
     */
    @OnOpen
    public void onOpen(@PathParam("principal") String principal, Session session) {
        // create observer 创建 观察者(观众)  --- 拿到观察者的sesion消息,后面通过session进行消息发送
        WebSocketObserver observer = new WebSocketObserver(session);
        // get subject 创建 被观察者(演员)
        WebSocketSubject subject = WebSocketSubject.Holder.getSubject(principal);//①
        // register observer into subject 被观察者(演员)登记观察者(观众)
        subject.addObserver(observer);//②
    }

①方法,创建或复用 Web Socket主题


/***
 * Web Socket 主题(被观察者)
 */
public class WebSocketSubject extends Observable {

    /**
     * subject键值
     */
    private String principal;

    private WebSocketSubject(String principal) {
        this.principal = principal;
    }
    
	/**
     * 通过不同字符(principal)来区分多个WebSocket主题
     * 不同主题下会有不同的观察者
     */
    public static class Holder {
        private static ConcurrentHashMap<String, WebSocketSubject> subjects = new ConcurrentHashMap<>();

        public static WebSocketSubject getSubject(String principal) {
            //判断是否存在该主题,有就复用,没有就新建
            if (subjects.containsKey(principal)) {
                return subjects.get(principal);
            }

            WebSocketSubject subject = new WebSocketSubject(principal);
            subjects.put(principal, subject);
            return subject;
        }
    }
}

② 建立观察者和被观察者的关系----将该主题下的被观察者存入集合中

public class Observable {
    //被登记的观察者(观众)都存在该集合
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    /**
     * Adds an observer to the set of observers for this object, provided
     * that it is not the same as some observer already in the set.
     * The order in which notifications will be delivered to multiple
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
}

2.当被观察者(演员)发生变化,通知观察者

public class WebSocketNoticeLZ {
    /**
     * 被观察者(演员)发生了变化,通知观察者(观众)
     * @param principal 通过该字符串找到不同的主题
     */
    public static void notice(Object object,String principal) {
        String type = "notice";
        JSONObject data = JSONObject.parseObject(JSONObject.toJSONString(object));
        //得到指定的主题
        WebSocketSubject subject = WebSocketSubject.Holder.getSubject(principal);
        //通知该主题下的所有观察者(观众)
        subject.notify(type, data.toJSONString());
    }
}
/***
 * Web Socket 具体主题(被观察者)
 */
public class WebSocketSubject extends Observable {
    /**
     * 通知观察者(观众)
     * 被观察者(演员)发生了变化,观察者(观众)也要做出相应变化
     * @param type
     * @param data
     */
    public void notify(String type, String data) {
        //被观察者对象状态发生了变化
        super.setChanged();
        JSONObject json = new JSONObject();
        json.put("type", type);
        json.put("data", data);
        //调用所有登记过的观察者的update方法
        super.notifyObservers(json.toJSONString());
    }
public class Observable {
    private boolean changed = false;
    //被登记的观察者(观众)都存在该集合
    private Vector<Observer> obs;
    
    /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }
    
    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--)
            //遍历所有观察者的update方法,进行发送消息
            ((Observer)arrLocal[i]).update(this, arg);
    }

3.通过观察者的session进行通知

/***
 * Web Socket 观察者.(具体观察者)
 */
public class WebSocketObserver implements Observer {

    /**
     * Web Socket session
     */
    private Session session;

    public WebSocketObserver(Session session) {
        this.session = session;
    }

    public Session getSession() {
        return session;
    }

    @Override
    public void update(Observable o, Object arg) {
        String message = (String) arg;
        try {
            if (session.isOpen()) {
                //发送消息
                session.getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            throw new RuntimeException("websocket sendText erro.", e);
        }
    }
}

三 、源码内容

//前端请求建立连接
@Component
@ServerEndpoint(value = "/websocket/{principal}")
public class WebSocketEndpoint {

    /**
     * Web Socket连接建立成功的回调方法
     * @param principal 通过该字符串找到不同的主题
     * @param session 观察者的session
     */
    @OnOpen
    public void onOpen(@PathParam("principal") String principal, Session session) {
        // create observer 观察者  --- 拿到观察者的sesion消息,后面通过session进行消息发送
        WebSocketObserver observer = new WebSocketObserver(session);
        // get subject 被观察者(演员)
        WebSocketSubject subject = WebSocketSubject.Holder.getSubject(principal);
        // register observer into subject 被观察者(演员)登记观察者(观众)
        subject.addObserver(observer);
    }
public class WebSocketNoticeLZ {
    /**
     * 服务端向客户端推送消息  hint true 提示框
     * @param principal 通过该字符串找到不同的主题
     */
    public static void notice(Object object,String principal) {
        String type = "notice";
        JSONObject data = JSONObject.parseObject(JSONObject.toJSONString(object));
        //通知被登记的观察者(观众)
        WebSocketSubject subject = WebSocketSubject.Holder.getSubject(principal);
        subject.notify(type, data.toJSONString());
    }
}
/***
 * Web Socket 主题(被观察者)
 */
public class WebSocketSubject extends Observable {

    /**
     * subject键值
     */
    private String principal;

    private WebSocketSubject(String principal) {
        this.principal = principal;
    }

    public String getPrincipal() {
        return principal;
    }

    /**
     * 通知观察者(观众)
     * 被观察者(演员)发生了变化,观察者(观众)也要做出相应变化
     * @param type
     * @param data
     */
    public void notify(String type, String data) {
        //被观察者对象状态发生了变化
        super.setChanged();
        JSONObject json = new JSONObject();
        json.put("type", type);
        json.put("data", data);
        //调用所有登记过的观察者的update方法
        super.notifyObservers(json.toJSONString());
    }
	/**
     * 通过不同字符(principal)来区分多个WebSocket主题
     * 不同主题下会有不同的观察者
     */
    public static class Holder {
        private static ConcurrentHashMap<String, WebSocketSubject> subjects = new ConcurrentHashMap<>();

        public static WebSocketSubject getSubject(String principal) {
            if (subjects.containsKey(principal)) {
                return subjects.get(principal);
            }

            WebSocketSubject subject = new WebSocketSubject(principal);
            subjects.put(principal, subject);
            return subject;
        }
    }
}
/***
 * Web Socket 观察者.</br>
 *
 * @attation 重写equals方法{@link #equals(Object)}
 *
 */
public class WebSocketObserver implements Observer {

    /**
     * Web Socket session
     */
    private Session session;

    public WebSocketObserver(Session session) {
        this.session = session;
    }

    public Session getSession() {
        return session;
    }

    @Override
    public void update(Observable o, Object arg) {
        String message = (String) arg;
        try {
            if (session.isOpen()) {
                //发送消息
                session.getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            throw new RuntimeException("websocket sendText erro.", e);
        }
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((session == null) ? 0 : session.getId().hashCode());
        return result;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值