【Java】23种设计模式——10.外观模式22.中介者模式15.观察者模式

接下来,我们将介绍三种模式:外观模式、中介者模式、观察模式。

模式

外观模式

外观模式(Facade Pattern)‌ 是一种 ‌结构型设计模式‌。通过提供统一的 ‌高层接口‌,隐藏子系统的复杂性,简化客户端与多个子系统之间的交互。其核心是 ‌封装复杂性‌,提供“一站式”调用入口。

外观模式的核心角色

角色 职责 示例场景
Facade(外观类‌) 封装子系统的复杂调用逻辑,提供简洁接口 HomeTheaterFacade(家庭影院控制中心)
Subsystems(子系统类‌) 实现具体功能,对外观类透明 灯光系统、音响系统、投影系统

适用场景

  • 复杂系统整合 多个子系统需要统一的入口(如电商下单流程)。
  • 分层架构设计 提供分层接口隔离底层实现(如框架API)。
  • 遗留系统适配 为新系统封装旧接口,减少迁移成本。

使用

设计一个家庭影院控制系统,用户只需要通过一个按钮即可完成“观影模式”(关灯、开投影、开影音)。

  • 子类系统 (灯光系统、投影仪系统、影响系统)

    /*
     * 子系统 : 灯光系统
     * @Author:lyj
     * @Date:2025/5/7 16:17
     */
    public class LightSystem {
        /**
         * 灯光调暗至观影模式
         */
        public void dimLight(){
            System.out.println("\uD83D\uDCA1 灯光调暗至观影模式");
        }
        /**
         * 灯光调亮至正常模式
         */
        public void brightLight(){
            System.out.println("\uD83D\uDCA1 灯光调亮至正常模式");
        }
        public void offLight(){
            System.out.println("\uD83D\uDCA1 关闭灯光");
        }
    }
    /*
     * 子系统 : 音响系统
     * @Author:lyj
     * @Date:2025/5/7 16:17
     */
    public class SoudSystem {
        /**
         * 启动环绕声模式
         */
        public void on()
        {
            System.out.println("\uD83D\uDD0A 音响系统启动,切换至环绕声模式");
        }
        /**
         * 设置音量
         * @param level
         */
        public void Volume(int level)
        {
            System.out.println("\uD83D\uDD0A 音量设置为:" + level);
        }
        /**
         * 关闭音响系统
         */
        public void off()
        {
            System.out.println("\uD83D\uDD0A 音响系统关闭");
        }
    }
    /**
     * 子系统 : 设置投影仪
     * @Author:lyj
     * @Date:2025/5/7 16:21
     */
    public class Projector {
        /**
         * 启动投影仪
         */
        public void start()
        {
            System.out.println("\uD83D\uDCFD\uFE0F 投影仪启动,切换到HDMI输入");
        }
    
        /**
         * 关闭投影仪
         */
        public void shutDown()
        {
            System.out.println("\uD83D\uDCFD\uFE0F 投影仪关闭");
        }
    }
    
  • 外观类 (家庭影院操作)

    /**
     * 外观类 : 家庭影院操作
     * @Author:lyj
     * @Date:2025/5/7 16:24
     */
    public class HomeTheaterFacade {
        private LightSystem lightSystem;
        private SoudSystem soudSystem;
        private Projector projector;
    
        public HomeTheaterFacade(){
            lightSystem = new LightSystem();
            soudSystem = new SoudSystem();
            projector = new Projector();
        }
    
        /**
         * 一键开启观影模式
         */
        public void startMovie(){
            lightSystem.dimLight();
            soudSystem.on();
            projector.start();
        }
    
        public void endMovie(){
            lightSystem.offLight();
            soudSystem.off();
            projector.shutDown();
        }
    }
    

    测试

    HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
    homeTheaterFacade.startMovie(); // 开启
    System.out.println("----------------------------------------------------------------------------");
    homeTheaterFacade.endMovie();   // 关闭
    

    运行结果如下:
    image

中介者模式

中介者模式:是23种设计模式中的行为模式,旨在通过封装对象间的交互逻辑,‌降低多个对象间的耦合度。‌

中介者模式的优势包括:

  • 降低耦合度 对象间无需直接引用,通信统一由中介者处理。
  • 集中控制交互逻辑 新增同事类时只需要改中介者,避免分散到各对象中 (如添加消息过滤功能)
  • 简化多对多关系 将复杂的网状通信变为星型结构(如飞机与塔台管制系统)

中介者模式核心思想

角色 职责
Mediator‌(中介者接口) 定义中介者接口,协调各对象间的通信(如聊天室转发消息)
ConcreteMediator‌(中介者实现类) 实现具体协调逻辑,管理并维护所有同事对象的引用
Colleague‌(同事接口类) 定义同事类接口,每个同事类持有中介者引用,通过中介者与其他同事通信

应用场景

  • GUI组件交互 窗口中的按钮、输入框等组件通过中介者协调事件(如点击触发输入框刷新)。
  • 航空管制系统 飞机与塔台通过中介者交换位置和状态信息,避免飞机间直接通信。
  • 游戏引擎角色交互 游戏角色通过中介者处理战斗、交易等交互逻辑。

使用

聊天室消息转发。

  • 中介者接口 (聊天室接口类)
/**
 * 中介者接口 : 聊天室通用接口
 * @Author:lyj
 * @Date:2025/5/13 18:07
 */
public interface ChatMediator {
    /**
     * 发送消息
     * @param msg 消息
     * @param user 发送用户
     */
    public void sendMessage(String msg, User user);
    /**
     * 添加用户
     * @param user 用户
     */
    public void addUser(User user);
}
  • 中介者实现类
/**
 * 具体中介者 : 聊天室
 * @Author:lyj
 * @Date:2025/5/13 18:11
 */
public class ChatRoomMediator implements ChatMediator {
    /**
     * 聊天室成员
     */
    private List<User>  users= new ArrayList<>();

    /**
     * 发送消息
     * @param msg 消息
     * @param sender 发送用户
     */
    @Override
    public void sendMessage(String msg, User sender) {
        for (User user : users) {
            if (user != sender) {       // 消息排除发送者自身
                user.receive(msg);
            }
        }
    }

    /**
     * 添加用户
     * @param user 用户
     */
    @Override
    public void addUser(User user) {
        users.add(user);
    }
}
  • 同事类
/**
 * 抽象同事类: 用户
 * @Author:lyj
 * @Date:2025/5/13 18:10
 */
public abstract class User {
    protected ChatMediator mediator;        // 聊天室
    protected String name;                  // 用户名

    /**
     * 构造函数
     * @param mediator 聊天室
     * @param name 用户名
     */
    public User(ChatMediator mediator, String name)
    {
        this.mediator = mediator;
        this.name = name;
    }

    /**
     * 发送消息
     * @param msg
     */
    public abstract void send(String msg);
    /**
     * 接收消息
     * @param msg 消息
     */
    public abstract void receive(String msg);
}
/**
 * 具体同事类(普通用户)
 * @Author:lyj
 * @Date:2025/5/13 18:16
 */
public class ChatUser extends User {
    /**
     * 构造函数
     *
     * @param mediator 聊天室
     * @param name     用户名
     */
    public ChatUser(ChatMediator mediator, String name) {
        super(mediator, name);
    }

    /**
     * 发送消息
     * @param msg 消息内容
     */
    @Override
    public void send(String msg) {
        System.out.println(this.name + "[发送消息]: =" + msg);
        mediator.sendMessage(msg, this);        // 通过中介者返送消息
        System.out.println("");
    }

    /**
     * 接收消息
     * @param msg 消息内容
     */
    @Override
    public void receive(String msg) {
        System.out.println(this.name + "[接收消息]: =" + msg);
    }
}

客户端使用:

// 创建中介者(聊天室)
ChatMediator chatRoom = new ChatRoomMediator();
// 创建用户并注册聊天室
ChatUser user1 = new ChatUser(chatRoom, "张三");
ChatUser user2 = new ChatUser(chatRoom, "李四");
ChatUser user3 = new ChatUser(chatRoom, "王五");
chatRoom.addUser(user1);
chatRoom.addUser(user2);
chatRoom.addUser(user3);

// 发送消息
user1.send("你好,我是张三");
user2.send("你好,我是李四");

运行结果:
image

观察者模式

观察者模式(Observer Pattern)‌ 是 GoF 23 种设计模式中的 ‌行为型模式‌,用于建立 ‌对象间一对多的依赖关系‌。当一个对象(被观察者/主题)状态变化时,所有依赖它的对象(观察者)都会收到通知并自动更新。

观察者模式优势:

  • 松耦合 被观察者无需了解观察者的具体实现,只需要接口交互(如气象站无需知道显示设备类型)
  • 动态订阅机制 可运行时时动态添加/移除观察者(如临时关闭某个显示设备)
  • 广播通讯能力 状态变化时自动通知所有观察者(如股票价格变动通知多个交易终端)

角色

角色 职责
Subject‌(抽象被观察者) 被观察者接口,定义注册观察者、删除观察者、通知观察者的方法
ConcreteSubject‌(具体被观察者) 具体被观察者,维护观察者列表,在状态变化时触发通知
Observer‌(抽象观察者) 观察者接口,定义接收通知后的响应方法(如更新数据)
ConcreteObserver‌(具体观察者) 具体观察者,实现响应逻辑(如刷新界面显示

使用场景

  • GUI事件监听 按钮点击事件触发多个监听的onClick() 方法
  • 消息队列系统 生产者发布消息,多个消费之订阅并处理消息。
  • 游戏引擎 角色血量变化触发UI血条、成就系统的更新。
  • 配置中心更新 配置修改后自动同步到所有微信服务模块。

使用

气象站数据更新通知

  • 抽象被观察者 (气象站通用接口)

    /**
     * 被观察者接口
     * @Author:lyj
     * @Date:2025/5/14 11:40
     */
    public interface Subject {
        /**
         * 注册观察者
         * @param observer 观察者
         */
        public void registerObserver(Observer observer);
        /**
         * 移除观察者
         * @param observer 观察者
         */
        public void removeObserver(Observer observer);
        /**
         * 通知观察者
         */
        public void notifyObservers();
    }
    
  • 具体被观察者 (气象站)

    /**
     * 被观察者 : 气象站
     * @Author:lyj
     * @Date:2025/5/14 11:51
     */
    public class WeatherSatation implements Subject {
        private List<Observer> observers = new ArrayList<>();
        private float temperature;
        /**
         * 注册观察者
         * @param observer 观察者
         */
        @Override
        public void registerObserver(Observer observer) {
            observers.add(observer);
        }
    
        /**
         * 移除观察者
         * @param observer 观察者
         */
        @Override
        public void removeObserver(Observer observer) {
            observers.remove(observer);
        }
    
        /**
         * 模拟温度变化,并触发通知
         */
        @Override
        public void notifyObservers() {
            for (Observer observer : observers) {
                observer.update(temperature);
            }
        }
    
        /**
         *  模拟温度变化,并触发通知
         * @param temperature 温度变化
         */
        public void setTemperature(float temperature) {
            this.temperature = temperature;
            notifyObservers();
            System.out.println();
        }
    }
    
  • 抽象观察者 (抽象观察者获取气温)

    /**
     * 观察者接口
     * @Author:lyj
     * @Date:2025/5/14 11:48
     */
    public interface Observer {
        /**
         * 更新天气
         * @param temperature 气温
         */
        public void update(float temperature);
    }
    
  • 具体观察者 (设备获取气温)

/**
 * 具体观察者 : 显示设备
 * @Author:lyj
 * @Date:2025/5/14 15:26
 */
public class DisplayDevice implements Observer {
    private String deviceName;      // 设备名称
    public DisplayDevice(String deviceName) {
        this.deviceName = deviceName;
    }
    /**
     * 更新数据(接收更新温度通知)
     * @param temperature 气温
     */
    @Override
    public void update(float temperature) {
        System.out.printf("[%s]收到通知,温度:%.1f°C\n" ,deviceName, temperature);
    }
}

运行客户端:

// 创建被观察者
WeatherSatation weatherSatation = new WeatherSatation();
// 创建观察者
Observer displayDevice1 = new DisplayDevice("手机显示屏");
Observer displayDevice2 = new DisplayDevice("电视显示屏");
// 注册观察者
weatherSatation.registerObserver(displayDevice1);
weatherSatation.registerObserver(displayDevice2);
// 模拟温度变化
weatherSatation.setTemperature(25f);
weatherSatation.setTemperature(30f);
// 移除一个观察者
weatherSatation.removeObserver(displayDevice1);
weatherSatation.setTemperature(27.6f);

运行结果:
image

扩展——发布订阅者模式

发布订阅模式,是观察者模式的升级版。

观察者模式VS发布订阅者模式,有什么区别和联系呢?

  • 观察者模式 :被观察者直接维护观察者列表,强耦合(如气象站直接调用update() 方法)
  • 发布-订阅者模式 : 通过消息代理(Broker) 解耦,发布者和订阅者互不知晓对方的存在(如 Kafka、MQIT)

关键差异对比

特征 观察者模式 发布-订阅者模式
耦合度 被观察者持有观察者引用 通过消息代理完全解耦
通信方式 同步调用update()方法 异步消息队列(可跨进程/网络)
扩展性 适合单机简单场景 适合分布式系统(如微服务)
posted @ 2025-06-20 16:30  陆陆无为而治者  阅读(45)  评论(0)    收藏  举报