6.Android 设计模式 五个核心之三:观察者模式 在项目中的实战

观察者模式:一对多依赖,主题状态变更自动通知所有观察者,实现解耦。

1.概念

观察者模式(Observer Pattern) 是一种行为型设计模式,定义对象间一种 一对多的依赖关系,当一个对象(被观察者/Subject)状态改变时,所有依赖它的对象(观察者/Observer)都会自动收到通知并更新。核心角色:

  • Subject(主题) :维护观察者列表,提供注册/注销接口,负责通知观察者
  • Observer(观察者) :定义更新接口,接收状态变更通知
  • ConcreteSubject & ConcreteObserver:具体实现类
2.在Android源码中的应用场景
  • LiveData
    LiveData 是被观察者,Activity/Fragment 作为观察者通过 observe() 注册,数据变化时自动更新 UI。
  • View 的事件监听
    View.setOnClickListener() 本质是观察者模式(View 是 Subject,OnClickListener 是 Observer)。
  • RecyclerView.Adapter
    Adapter 是被观察者,调用 notifyDataSetChanged() 通知 RecyclerView(观察者)刷新视图。
  • BroadcastReceiver
    系统广播中心是被观察者,接收器(BroadcastReceiver)作为观察者监听广播。
3.UML图
4.语音项目的例子和没用设计模式的对比

场景:语音识别模块识别到指令后,需同时更新 UI、保存日志、控制设备。

4.1 未使用观察者模式(耦合代码)
typescript 复制代码
public class CloudService {
    public static void syncCommand(String command) {

    }
}
java 复制代码
public class DatabaseEngine {
    // 使用 SQLiteOpenHelper 获取数据库实例
    private final SQLiteDatabase db;
    private final DatabaseHelper dbHelper; // 自定义的 SQLiteOpenHelper

    public DatabaseEngine(Context context) {
        // 正确初始化:通过 DatabaseHelper 获取数据库实例
        this.dbHelper = new DatabaseHelper(context);
        this.db = dbHelper.getWritableDatabase();
    }

    public void logVoiceCommand(String command) {
        ContentValues values = new ContentValues();
        values.put("command", command);
        values.put("timestamp", System.currentTimeMillis());

        try {
            // 插入数据到 voice_logs 表
            db.insert("voice_logs", null, values);
        } catch (Exception e) {
            Log.e("DatabaseEngine", "保存失败: " + e.getMessage());
        }
    }

    // 关闭数据库连接(可选)
    public void close() {
        if (db != null && db.isOpen()) {
            db.close();
        }
        if (dbHelper != null) {
            dbHelper.close();
        }
    }
}
java 复制代码
public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "voice_commands.db";
    private static final int DATABASE_VERSION = 1;

    // 表结构定义
    private static final String CREATE_TABLE_VOICE_LOGS =
            "CREATE TABLE voice_logs (" +
                    "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                    "command TEXT NOT NULL," +
                    "timestamp INTEGER NOT NULL)";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建表
        db.execSQL(CREATE_TABLE_VOICE_LOGS);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 简单处理:删除旧表,创建新表
        db.execSQL("DROP TABLE IF EXISTS voice_logs");
        onCreate(db);
    }
}
csharp 复制代码
public class SmartDeviceController {
    // 模拟物联网设备控制
    public void turnOnLights() {
        Log.i("DeviceCtrl", "执行:打开灯光");
        // 实际硬件控制代码(如MQTT/HTTP请求)
    }

    public void turnOffLights() {
        Log.i("DeviceCtrl", "执行:关闭灯光");
    }

    // 扩展方法示例
    public void increaseTemperature() {
        Log.i("DeviceCtrl", "温度升高1℃");
    }
}
arduino 复制代码
public class UIUpdater {
    private final TextView commandTextView; // Android视图组件

    public UIUpdater(TextView textView) {
        this.commandTextView = textView;
    }

    public void updateCommandText(String text) {
        // 确保在主线程更新UI
        commandTextView.post(() -> {
            commandTextView.setText(text);
            commandTextView.setTextColor(Color.GREEN);
        });
    }
}
csharp 复制代码
public class VoiceRecognizer {
    // 1. UI更新类(替换TextView)
    private UIUpdater uiUpdater;
    // 2. 数据库引擎类(替换DatabaseHelper)
    private DatabaseEngine databaseEngine;
    // 3. 设备控制器(保留原类)
    private SmartDeviceController deviceController;

    public VoiceRecognizer(
            UIUpdater uiUpdater,
            DatabaseEngine databaseEngine,
            SmartDeviceController deviceController
    ) {
        this.uiUpdater = uiUpdater;
        this.databaseEngine = databaseEngine;
        this.deviceController = deviceController;
    }

    // 语音识别回调
    public void onVoiceCommandReceived(String command) {
        // 1. 更新UI
        uiUpdater.updateCommandText("识别结果: " + command);

        // 2. 保存到数据库
        databaseEngine.logVoiceCommand(command);

        // 3. 控制设备
        if ("打开灯光".equals(command)) {
            deviceController.turnOnLights();
        } else if ("关闭灯光".equals(command)) {
            deviceController.turnOffLights();
        }

        // 4. 新功能:网络同步(紧耦合)
        new Thread(() -> CloudService.syncCommand(command)).start();
    }
}
4.2 使用观察者模式
typescript 复制代码
// 云端同步观察者
public class CloudSyncerObs implements VoiceObserverObs {
    @Override
    public void onVoiceCommandReceived(String command) {
        new Thread(() -> {
            Log.i("CloudSyncerObs", "同步命令: " + command);
            // 实际网络请求代码...
        }).start();
    }
}
typescript 复制代码
// 数据库日志观察者
public class DatabaseLoggerObs implements VoiceObserverObs {
    private final DatabaseEngine dbEngine; // 未改名

    public DatabaseLoggerObs(Context context) {
        this.dbEngine = new DatabaseEngine(context);
    }

    @Override
    public void onVoiceCommandReceived(String command) {
        dbEngine.logVoiceCommand(command);
    }

    public void closeDatabase() {
        dbEngine.close();
    }
}
csharp 复制代码
// 设备控制观察者
public class DeviceControllerObs implements VoiceObserverObs {
    private final SmartDeviceController device; // 未改名

    public DeviceControllerObs() {
        this.device = new SmartDeviceController();
    }

    @Override
    public void onVoiceCommandReceived(String command) {
        if ("打开灯光".equals(command)) {
            device.turnOnLights();
        } else if ("关闭灯光".equals(command)) {
            device.turnOffLights();
        }
    }
}
java 复制代码
// UI更新观察者
public class UiUpdaterObs implements VoiceObserverObs {
    private final TextView commandTextView;

    public UiUpdaterObs(TextView textView) {
        this.commandTextView = textView;
    }

    @Override
    public void onVoiceCommandReceived(String command) {
        commandTextView.post(() -> {
            commandTextView.setText("命令: " + command);
            commandTextView.setTextColor(Color.GREEN);
        });
    }
}
arduino 复制代码
public interface VoiceObserverObs {
    void onVoiceCommandReceived(String command);
}
typescript 复制代码
// 2. 被观察者(主题)
public class VoiceRecognizerObs {
    private final List<VoiceObserverObs> observers = new ArrayList<>();

    public void registerObserver(VoiceObserverObs observer) {
        if (!observers.contains(observer)) {
            observers.add(observer);
        }
    }

    public void unregisterObserver(VoiceObserverObs observer) {
        observers.remove(observer);
    }

    private void notifyObservers(String command) {
        for (VoiceObserverObs observer : observers) {
            observer.onVoiceCommandReceived(command);
        }
    }

    // 语音识别回调
    public void onVoiceCommandReceived(String command) {
        Log.i("VoiceRecognizerObs", "识别命令: " + command);
        notifyObservers(command);
    }
}

具体的调用逻辑:

scss 复制代码
public class MainActivity extends AppCompatActivity {

    private VoiceRecognizerObs voiceRecognizer;
    private DatabaseLoggerObs dbLogger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        TextView resultView = findViewById(R.id.tv_page_name);

        // 创建被观察者(Obs版本)
        voiceRecognizer = new VoiceRecognizerObs();

        // 创建并注册观察者(Obs版本)
        voiceRecognizer.registerObserver(new UiUpdaterObs(resultView));
        voiceRecognizer.registerObserver(new DatabaseLoggerObs(this));
        voiceRecognizer.registerObserver(new DeviceControllerObs());
        voiceRecognizer.registerObserver(new CloudSyncerObs());

        // 模拟语音识别
        simulateVoiceRecognition();
    }

    private void simulateVoiceRecognition() {
        new Handler().postDelayed(() -> {
            // 发送命令1
            voiceRecognizer.onVoiceCommandReceived("打开灯光");

            // 2秒后发送命令2
            new Handler().postDelayed(() -> {
                voiceRecognizer.onVoiceCommandReceived("关闭灯光");
            }, 2000);
        }, 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 清理资源
        if (dbLogger != null) {
            dbLogger.closeDatabase();
        }
    }
}
4.3 架构图
4.4.数据流向图

分析如下的区别.使用观察者和没有使用观察者的区别!

java 复制代码
// UI更新观察者
public class UiUpdaterObs implements VoiceObserverObs {
    private final TextView commandTextView;

    public UiUpdaterObs(TextView textView) {
        this.commandTextView = textView;
    }

    @Override
    public void onVoiceCommandReceived(String command) {
        commandTextView.post(() -> {
            commandTextView.setText("命令: " + command);
            commandTextView.setTextColor(Color.GREEN);
        });
    }
}
arduino 复制代码
public class UIUpdater {
    private final TextView commandTextView; // Android视图组件

    public UIUpdater(TextView textView) {
        this.commandTextView = textView;
    }

    public void updateCommandText(String text) {
        // 确保在主线程更新UI
        commandTextView.post(() -> {
            commandTextView.setText(text);
            commandTextView.setTextColor(Color.GREEN);
        });
    }
}

调用的区别

java 复制代码
**观察者模式中的使用**:
// 注册观察者
voiceRecognizerObs.registerObserver(new UiUpdaterObs(textView));

// 语音识别回调自动触发UI更新
voiceRecognizerObs.onVoiceCommandReceived("打开灯光");


**非观察者模式中的使用**:
// 初始化工具类
UIUpdater uiUpdater = new UIUpdater(textView);

// 需要显式调用
voiceRecognizer.onVoiceCommandReceived("打开灯光", () -> {
    uiUpdater.updateCommandText("命令: 打开灯光");
});

最终结论

  • 选择 UiUpdaterObs (被动调用,收到外面的事件),当您需要构建事件驱动的响应式架构,特别是在复杂系统中处理状态变化通知
  • 选择 UIUpdater(主动调用,外面手动调用这个方法) 当您需要简单通用的UI更新工具,不涉及复杂的事件传递机制
  • 在大型项目中,推荐使用观察者模式变体以获得更好的可维护性和扩展性
5.优点
  1. 解耦:被观察者与观察者松耦合,互不依赖具体实现
  2. 动态扩展:可运行时添加/删除观察者(如注册/注销广播)
  3. 符合开闭原则:新增观察者无需修改被观察者代码
  4. 事件驱动:适用于异步场景(如网络回调、传感器数据)
  5. 数据一致性:确保所有依赖对象状态同步更新
6.和相似的设计模式的区别
模式 观察者模式 发布-订阅模式 中介者模式
耦合度 观察者与被观察者直接交互 通过消息中心解耦 对象通过中介者间接交互
通信方向 单向(Subject → Observer) 双向(Pub/Sub 可互发消息) 多向(中介协调所有对象)
实时性 实时同步通知 可异步(消息队列) 实时
Android 案例 LiveData、OnClickListener EventBus、RxJava Fragment 间通过 Activity 通信

关键区别

  • 观察者 vs 发布-订阅

    观察者模式是 直接调用 (如 listener.onClick()),发布-订阅通过 中间代理 (如 EventBus 的 post(event))。

  • 观察者 vs 中介者

    观察者处理 一对多通知 ,中介者解决 多对象复杂交互(如多个 Fragment 状态同步)。

总结:

观察者模式:一对多依赖,主题状态变更自动通知所有观察者,实现解耦。

相关推荐
子兮曰5 小时前
后端字段又改了?我撸了一个 BFF 数据适配器,从此再也不怕接口“屎山”!
前端·javascript·架构
卓卓不是桌桌7 小时前
如何优雅地处理 iframe 跨域通信?这是我的开源方案
javascript·架构
Qlly7 小时前
DDD 架构为什么适合 MCP Server 开发?
人工智能·后端·架构
用户881586910911 天前
AI Agent 协作系统架构设计与实践
架构
鹏北海1 天前
Qiankun 微前端实战踩坑历程
前端·架构
货拉拉技术1 天前
货拉拉海豚平台-大模型推理加速工程化实践
人工智能·后端·架构
RoyLin1 天前
libkrun 深度解析:架构设计、模块实现与 Windows WHPX 后端
架构
CoovallyAIHub2 天前
实时视觉AI智能体框架来了!Vision Agents 狂揽7K Star,延迟低至30ms,YOLO+Gemini实时联动!
算法·架构·github
RoyLin2 天前
领域驱动设计:回归本质的工程实践
架构
CoovallyAIHub2 天前
OpenClaw:从“19万星标”到“行业封杀”,这只“赛博龙虾”究竟触动了谁的神经?
算法·架构·github