委托模式:把活儿派出去的艺术
委托模式,说白了就是「我不干,你干」。
我刚开始接触这个模式时,觉得它太简单了——不就是A调用B的方法吗?后来在项目中踩过几次坑,才真正理解它的价值。你想想看,如果所有事情都自己扛,类会变得臃肿不堪。委托模式就是帮你把职责分出去,让代码更灵活。
什么是委托模式?
委托模式是一种行为设计模式。它让一个对象(委托者)把某些任务交给另一个对象(被委托者)去完成。委托者持有被委托者的引用,调用它的方法,就像自己亲自做了一样。
嗯,这里要注意:委托和继承不同。继承是「我是」,委托是「我有」。我习惯用委托代替继承,因为组合比继承更灵活。
核心三要素:
- 委托接口:定义双方约定的契约
- 委托者:持有委托接口引用,调用方法
- 被委托者:实现委托接口,真正干活
一个简单的例子
假设你是一个项目经理,手头有个需求要开发。你不可能自己写代码,于是你把任务委托给程序员。项目经理就是委托者,程序员就是被委托者。
先看Java版本:
// 委托接口
interface Task {
void execute();
}
// 被委托者:程序员
class Programmer implements Task {
@Override
public void execute() {
System.out.println("程序员正在写代码...");
}
}
// 委托者:项目经理
class ProjectManager {
private Task task;
public ProjectManager(Task task) {
this.task = task;
}
public void assignTask() {
System.out.println("项目经理分配任务");
task.execute(); // 委托给程序员
}
}
// 使用
public class Main {
public static void main(String[] args) {
Programmer programmer = new Programmer();
ProjectManager pm = new ProjectManager(programmer);
pm.assignTask();
}
}
C++版本也差不多:
#include <iostream>
#include <memory>
// 委托接口
class Task {
public:
virtual void execute() = 0;
virtual ~Task() = default;
};
// 被委托者
class Programmer : public Task {
public:
void execute() override {
std::cout << "程序员正在写代码..." << std::endl;
}
};
// 委托者
class ProjectManager {
private:
std::shared_ptr<Task> task;
public:
ProjectManager(std::shared_ptr<Task> t) : task(t) {}
void assignTask() {
std::cout << "项目经理分配任务" << std::endl;
task->execute();
}
};
int main() {
auto programmer = std::make_shared<Programmer>();
ProjectManager pm(programmer);
pm.assignTask();
return 0;
}
事件处理与回调机制
委托模式最常见的应用场景就是事件处理和回调。我在项目中遇到过很多次:UI按钮点击、网络请求完成、定时器触发……这些本质上都是委托。
Java里的事件监听器就是典型的委托模式。你注册一个监听器(被委托者),事件源(委托者)在事件发生时调用你的方法。
// 事件监听接口
interface OnClickListener {
void onClick(View view);
}
// 按钮(委托者)
class Button {
private OnClickListener listener;
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
public void click() {
if (listener != null) {
listener.onClick(this);
}
}
}
// 使用
public class App {
public static void main(String[] args) {
Button button = new Button();
button.setOnClickListener(view -> {
System.out.println("按钮被点击了!");
});
button.click();
}
}
C++里可以用函数指针或std::function实现回调:
#include <iostream>
#include <functional>
class Button {
private:
std::function<void()> callback;
public:
void setOnClick(std::function<void()> cb) {
callback = cb;
}
void click() {
if (callback) {
callback();
}
}
};
int main() {
Button btn;
btn.setOnClick([]() {
std::cout << "按钮被点击了!" << std::endl;
});
btn.click();
return 0;
}
我的习惯:在Java中,我倾向于用接口定义回调;在C++中,我更喜欢用std::function,因为它更灵活,可以绑定lambda、函数指针、成员函数等。
委托模式的核心逻辑
下面这张图展示了委托模式的整体结构:
委托 vs 继承
我经常被问到:什么时候用委托,什么时候用继承?
| 对比维度 | 委托 | 继承 |
|---|---|---|
| 关系 | 「有」的关系(has-a) | 「是」的关系(is-a) |
| 耦合度 | 低,通过接口解耦 | 高,子类依赖父类 |
| 灵活性 | 运行时可以替换被委托者 | 编译时确定,不易改变 |
| 代码复用 | 通过组合复用 | 通过继承复用 |
| 适用场景 | 行为需要动态变化 | 行为稳定,层次明确 |
避坑指南:我曾经在一个项目中过度使用继承,导致类层次深达6层,改一个父类方法,所有子类都要跟着改。后来重构时全部改成委托,代码瞬间清爽了。记住:能用组合就别用继承。
实际项目中的应用
我在做Android开发时,经常用委托模式处理复杂的UI交互。比如一个列表项有多种点击行为:点赞、评论、分享。如果每个行为都写在Activity里,代码会变得又臭又长。
我的做法是:
- 定义一个点击事件接口,包含各种行为方法
- 创建不同的被委托者实现这个接口(点赞处理器、评论处理器等)
- 在列表适配器里持有委托接口,根据位置动态切换被委托者
这样,新增一种点击行为只需要新增一个被委托者类,完全不用改现有代码。这就是开闭原则的体现。
委托模式的优缺点
优点:
- 降低耦合度,委托者和被委托者通过接口交互
- 提高灵活性,运行时可以动态替换被委托者
- 符合单一职责原则,每个类只做一件事
- 便于单元测试,可以轻松mock被委托者
缺点:
- 增加类的数量,需要额外定义接口和实现类
- 如果委托链过长,调试起来会比较麻烦
- 性能上有微小开销(方法调用多了一层)
我的建议:不要为了用委托而用委托。如果一个类只有一两个方法需要委托,直接写个回调函数就够了。当委托逻辑变得复杂时,再考虑提取成接口。
总结
委托模式不是什么高深的技术,它就是「把活儿派出去」的编程思想。你想想看,现实中的项目经理不也是这样吗?他们不会自己写代码,而是把任务分配给合适的人。
在代码里,委托模式让我们的类更专注、更灵活。事件监听、回调函数、策略模式……这些本质上都是委托的变体。掌握了委托模式,你就掌握了软件设计中「分而治之」的精髓。
嗯,最后说一句:写代码就像带团队,懂得委托的人,才能写出好代码。
公众号:蓝海资料掘金营,微信deep3321