自学内容网 自学内容网

Java设计模式—观察者模式

观察者模式

1、什么是观察者模式?

  - 实例:现实生活中很多事物都是依赖存在的,一个发生变化会影响很多事物。比如油价上涨,关系很多企业,很多家庭;红绿灯发生变化时,人们会停止,会前进等。
  - 观察者模式 (Observer Pattern) :是一种一对多的依赖关系,让多个观察对象同时监听某一个主题对象,当主题对象的状态发生变化时,会自动通知所有观察者,使得它们能够自动更新自己。适用于当一个对象的状态发生改变时,所有依赖于它的对象都需要得到通知的情况。
  - 观察者模式具体的角色 :实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

  • 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
  • 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
  • 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  • 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

2、观察者模式优缺点及注意事项?

- 优点:
  1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系.符合依赖倒置原则
  2.目标与观察者之间建立了一套触发机制
- 缺点:
  1.目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
  2.当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
- 注意事项:
  1.JDK8 中java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,后面版本舍弃了,因为在使用异步处理的情况下,线程不安全。
  2.要注意循环调用情况,避免死锁。
  3.可以去观察消息队列实现,典型的观察者模式实现。

3、观察者模式实现?

模仿jdk8中 Observable 类 和 Observer接口实现
- 目标类

public class Subject {
    private Vector<Observer> obs = new Vector<>();

    public void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.add(o);
        }
    }

    public void deleteObserver(Observer o) {
        obs.remove(o);
    }

    public void notifyObservers() {
        for(Observer observer : this.obs) {
            observer.resopnse();
        }
    }
}

- 具体实现目标类

public class ConcreteSubject extends Subject{

    // 具体实现
    public void doSomethings(){
        // 一些其它逻辑处理

        System.out.println("-----被观察者发生了改变------");
        super.notifyObservers();
    }
}

- 观察者类

public interface Observer {
    // 响应
    void resopnse();
}

- 具体观察者类

public class ConcreteObserver implements Observer{
    private int num;
    
    public ConcreteObserver(int num) {
        this.num = num;
    }
    @Override
    public void resopnse() {
        System.out.println("观察者"+num+"做出改变");
    }
}

- 执行任务

public class Main {

    public static void main(String[] args) {
// ---------------  观察者模式  -----------------
        // 先创建多个观察者
        ConcreteSubject concreteSubject = new ConcreteSubject();
        for (int a = 0; a < 10; a++) {
            concreteSubject.addObserver(new ConcreteObserver(a+1));
        }
        concreteSubject.doSomethings();
    }

4、手写线程安全的观察者模式?

设定场景:手机花费快没钱了,运营商通知你要缴费了。 这里运营商就是被观察者,用户就是观察者,用户的手机号就是注册的一个过程,运营商通知用户。

使用jdk中com.util.concurrent提供的线程安全的类:CountDownLatch,CountDownLatch就一个线程同步工具,它相当于一个倒序计数器,用来协调多个线程的执行。多个线程通过调用它们所共享的计数器CountDownLatch的countDown方法来让计数器减1。通过await方法来阻塞当前线程,直到计数器变成0。达到线程阻塞直至其他线程执行完成被重新唤醒。主要有三个方法:
1.构造函数,初始化state的值,state等于同步线程数
2.await(),让线程阻塞
3.countDown(),计数器(state)减1的方法。

public class Main {

    public static void main(String[] args) {
// ---------------  安全的观察者模式 --------------
        // 设定场景:手机花费快没钱了,运营商通知你要缴费了。  这里运营商就是被观察者,用户就是观察者,用户的手机号就是注册的一个过程,运营商通知用户。
        ConcreteTelecomOperator telecomOperator = new ConcreteTelecomOperator("中国电信");
        telecomOperator.addUser(new ConcreteUser("张三"));
        telecomOperator.addUser(new ConcreteUser("李四"));
        telecomOperator.addUser(new ConcreteUser("王五"));
        telecomOperator.addUser(new ConcreteUser("小明"));
        telecomOperator.doSomeThing("通知您,您已欠费请及时缴费。祝你生活愉快!");
    }
}
/**
 * @program: practice_tools
 * @description: 运营商目标类
 * @author: tiezhu
 * @create: 2025-01-20 09:58
 **/
public class TelecomOperator {

    private String name;

    private ConcurrentMap<String, User> obs;

    public TelecomOperator(String name) {
        this.name = name;
        obs = new ConcurrentHashMap<>();
    }

    public void addUser(User u){
        if (u == null)
            throw new NullPointerException();
        if (!obs.containsKey(u.getName())) {
            obs.put(u.getName(),u);
        }
    }

    public void removeUser(User u){
        obs.remove(u.getName());
    }

    public void notifyObservers(String content) {
        try {
            long beginTime = System.currentTimeMillis();
            CountDownLatch latch = new CountDownLatch(obs.size());
            for (User user : obs.values()) {
                user.response(content);
                latch.countDown();
            }
            latch.await();
            long endTime = System.currentTimeMillis();
            System.out.println(name + "消息发送完毕,耗时:" + (endTime - beginTime));
            System.out.println();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * @program: practice_tools
 * @description: 具体目标类
 * @author: tiezhu
 * @create: 2025-01-20 11:43
 **/
public class ConcreteTelecomOperator extends TelecomOperator{

    public ConcreteTelecomOperator(String name) {
        super(name);
    }
    public void doSomeThing(String content){
        System.out.println("开始发布信息:");
        super.notifyObservers(content);
    }
}
/**
 * 用户观察者
 */
public interface User {
    void response(String content);

    String getName();
}
/**
 * @program: practice_tools
 * @description: 观察者实体类
 * @author: tiezhu
 * @create: 2025-01-20 10:04
 **/
public class ConcreteUser implements User{

    private String name;

    public ConcreteUser(String name) {
        this.name = name;
    }
    @Override
    public void response(String content) {
        System.out.println("接收到了消息为:"+content);
        System.out.println(name + "准备去缴费了");
    }
    @Override
    public String getName() {
        return name;
    }
}

结果:执行结果和创建顺序不同,不用按照创建顺序执行完再执行了。
在这里插入图片描述


原文地址:https://blog.csdn.net/weixin_41624547/article/details/145204159

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!