简介

  • 是一种行为型设计模式,

  • 用于定义一种一对多的依赖关系,当一个对象的状态发生变化时,它的所有依赖者(观察者)都会受到通知并自动更新。观察者模式促进了松耦合,使主题和观察者之间的关系更加灵活。

主要组成部分:

  • 主题(Subject):也称为被观察者,它维护一组观察者对象,提供注册和删除观察者的方法,并在状态发生变化时通知观察者。

  • 观察者(Observer):观察者是依赖于主题的对象,它定义一个更新接口,主题在状态变化时调用这个接口来通知观察者。

  • 具体主题(Concrete Subject):具体主题是主题的具体实现,它维护状态并在状态变化时通知观察者。

  • 具体观察者(Concrete Observer):具体观察者是观察者的具体实现,它实现了更新接口来响应主题的通知,以执行相应的操作。

工作流程

  • 主题维护一个观察者列表。

  • 观察者可以通过注册方法将自己添加到主题的观察者列表中。

  • 主题在状态发生变化时,遍历观察者列表,依次通知每个观察者。

  • 每个观察者接收到通知后,执行相应的操作。

例子

结构

代码

手写observer

主题(Subject)
/**
 * @author: lpy
 * @Date: 2023/10/21
 * @desc: boss
 */
@Data
public abstract class Subject {
    private String name;

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

    /** 同事列表*/
    private List<Observer> empls =new ArrayList<>();
    /** 增加同事*/
    public void addEmpl(Observer o){
        empls.add(o);
    }
    /** 减少同事*/
    public void removeEmpl(Observer o){
        empls.remove(o);
    }

    /**默认通知*/
    public void notifyEmpl(){
        empls.forEach(o->{
            o.update();
        });
    }

    private String action;
}
观察者(Observer)
/**
 * @author: lpy
 * @Date: 2023/10/21
 * @desc: 抽象观察者
 */
@Data
public abstract class Observer {
    private String name;
    private Subject subject;

    public Observer(String name, Subject sub){
        this.name = name;
        this.subject = sub;
    }

    public abstract void update();
}
具体主题(Concrete Subject)
/**
 * @author: lpy
 * @Date: 2023/10/21
 * @desc: 小秘
 */
public class Secretary extends Subject {
    public Secretary(String name) {
        super(name) ;
    }
}
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class Boss extends Subject{
    public Boss(String name) {
        super(name);
    }
}
具体观察者(Concrete Observer)
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class NBAEmplObserver extends Observer{
    public NBAEmplObserver(String name, Subject sub) {
        super(name, sub);
    }

    @Override
    public void update() {
        System.out.println("我是"+super.getSubject().getName()+","+super.getSubject().getAction()+","+super.getName()+"赶紧关闭nba,赶紧去工作");
    }
}
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class SimpleEmplObserver extends Observer{
    public SimpleEmplObserver(String name, Subject sub) {
        super(name, sub);
    }

    @Override
    public void update() {
        System.out.println("我是"+super.getSubject().getName()+","+super.getSubject().getAction()+","+super.getName()+"赶紧去工作");
    }
}
测试类
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class ManualObserverTest {
    public static void main(String[] args) {
        Secretary xm = new Secretary("小秘");

        Observer lw = new NBAEmplObserver("老王", xm);
        Observer lwb = new SimpleEmplObserver("老王八", xm);

        xm.addEmpl(lw);
        xm.addEmpl(lwb);

        xm.setAction("小秘回来啦");
        xm.notifyEmpl();
    }
}

jdk原生observer

主题(Subject)

这一层其实是可有可无的,jdk提供了Observable,加这层是为了松耦合。在观察者有个强转哪里会用到。 否则那里要强转成Boss2或者Secretary2类,具体类中耦合了具体类,这就没有针对接口编程了。

/**
 * @author: lpy
 * @Date: 2023/10/21
 */
@Data
public class Subject2 extends Observable {
    private String name;

    private String action;
    public Subject2(String name){
        this.name = name;
    }

    public void setAction(String v){
        this.action = v;
        setChanged();
        notifyObservers();
    }
    @Override
    public void setChanged(){
        super.setChanged();
    }
}
观察者(Observer)

java.util包下的Observer

具体主题(Concrete Subject)
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class Secretary2 extends Subject2 {

    public Secretary2(String name){
        super(name);
    }
}
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class Boss2 extends Subject2 {

    public Boss2(String name) {
        super(name);
    }
}
具体观察者(Concrete Observer)
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
@Data
public class NBAEmplObserver2 implements Observer {
    private String name;
    public NBAEmplObserver2(String  name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        Subject2 s = (Subject2) o;
        System.out.println(s.getName()+","+s.getAction()+","+this.getName()+"赶紧工作");
    }
}
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
@Data
public class SimpleEmplObserver2 implements Observer {
    private String name;

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

    @Override
    public void update(Observable o, Object arg) {
        Subject2 s = (Subject2) o;
        System.out.println(s.getName()+","+s.getAction()+","+this.getName()+"赶紧工作");
    }
}
测试类
/**
 * @author: lpy
 * @Date: 2023/10/21
 */
public class JDKObserverTest {
    public static void main(String[] args) {
        Secretary2 s2 = new Secretary2("小秘2号");

        NBAEmplObserver2 ne = new NBAEmplObserver2("孙悟空");
        SimpleEmplObserver2 se = new SimpleEmplObserver2("猪刚鬣");

        s2.addObserver(ne);
        s2.addObserver(se);

        s2.setAction("回来了");
//        如果在通知的话一定要setChanged
//        s2.setChanged();
//        s2.notifyObservers();
    }
}

应用

  • 事件处理系统:GUI编程中,用户交互事件(如点击按钮)会通知事件处理程序执行相应操作。

  • 订阅发布系统:用于发布者发布消息,订阅者订阅感兴趣的消息,并在消息到达时执行相应操作。

  • 消息通知系统:当一个系统需要向多个用户发送通知消息时,可以使用观察者模式来实现消息通知。

  • 数据更新通知:当数据源的数据发生变化时,通知多个依赖该数据的观察者。

观察者模式优缺点

  • 优点:

    • 松耦合:观察者模式实现了主题和观察者之间的松耦合关系。主题不需要知道观察者的具体细节,只需要通知观察者即可。这增加了系统的可维护性和扩展性。

    • 一对多关系:观察者模式支持一对多的依赖关系,允许一个主题对象通知多个观察者。这使得数据的发布和广播变得简单。

    • 通知机制:观察者模式提供了一种通知机制,主题状态发生变化时,自动通知所有依赖该主题的观察者,保持了对象之间的同步。

    • 开闭原则:添加新的观察者和主题是相对容易的,不需要修改现有代码,符合开闭原则

  • 缺点:

    • 过多的通知:如果观察者众多,频繁的通知可能会导致性能问题。因此,在某些情况下,应谨慎使用观察者模式。

    • 可能引发循环依赖:观察者模式中的主题和观察者之间可能存在循环依赖,需要小心处理以避免问题。

    • 复杂性增加:引入观察者模式可能会增加代码复杂性,因为它涉及多个类和接口,需要额外的结构。

7 大设计原则

不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;

- 单一职责原则(Single Responsibility Principle - SRP):
这个原则规定一个类应该只有一个改变的理由,也就是说,一个类应该只有一个单一的职责。这有助于保持类的简单性和可维护性,因为每个类只需关注一个特定的功能。

- 开放-封闭原则(Open-Closed Principle - OCP):
开放-封闭原则要求系统中的软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改原有代码。

- 里氏替换原则(Liskov Substitution Principle - LSP):
里氏替换原则规定,子类应该能够替换其父类,而不会影响程序的正确性。也就是说,子类应该继承父类的行为,但可以扩展或修改该行为,但不应该改变父类的行为。

- 接口隔离原则(Interface Segregation Principle - ISP):
接口隔离原则要求一个类不应该强迫其客户端依赖于它们不需要的接口。应该根据客户端的需求定义小而精确的接口,而不是大而笨重的接口。

- 依赖倒置原则(Dependency Inversion Principle - DIP):
依赖倒置原则要求高级模块不应该依赖于低级模块,它们都应该依赖于抽象。具体来说,这意味着应该通过抽象接口或抽象类来定义模块之间的依赖关系,而不是直接依赖于具体实现。

- 迪米特法则(Law of Demeter - LoD):
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

- 合成复用原则(Composite Reuse Principle - CRP):
合成复用原则鼓励通过组合(合成)已有的类来实现新功能,而不是通过继承现有的类。这样可以降低系统的耦合度,并且更加灵活。

源码位置

全部源码github.com/xs-alpha/designPattern

参考文献

如有内容侵权,麻烦联系博主删除

浙公网安备33011302000604

辽ICP备20003309号