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

0 阅读6分钟

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

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图

deepseek_mermaid_20250703_b1a987.png

4.语音项目的例子和没用设计模式的对比

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

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

    }
}
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();
        }
    }
}
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);
    }
}
public class SmartDeviceController {
    // 模拟物联网设备控制
    public void turnOnLights() {
        Log.i("DeviceCtrl", "执行:打开灯光");
        // 实际硬件控制代码(如MQTT/HTTP请求)
    }

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

    // 扩展方法示例
    public void increaseTemperature() {
        Log.i("DeviceCtrl", "温度升高1℃");
    }
}
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);
        });
    }
}
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 使用观察者模式
// 云端同步观察者
public class CloudSyncerObs implements VoiceObserverObs {
    @Override
    public void onVoiceCommandReceived(String command) {
        new Thread(() -> {
            Log.i("CloudSyncerObs", "同步命令: " + command);
            // 实际网络请求代码...
        }).start();
    }
}
// 数据库日志观察者
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();
    }
}
// 设备控制观察者
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();
        }
    }
}
// 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);
        });
    }
}
public interface VoiceObserverObs {
    void onVoiceCommandReceived(String command);
}
// 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);
    }
}

具体的调用逻辑:

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 架构图

deepseek_mermaid_20250703_8aba7e.png

4.4.数据流向图

deepseek_mermaid_20250703_e1a0e6.png

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

// 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);
        });
    }
}
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);
        });
    }
}

调用的区别

**观察者模式中的使用**:
// 注册观察者
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、OnClickListenerEventBus、RxJavaFragment 间通过 Activity 通信

关键区别

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

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

总结:

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