DRY 原则方法论:如何让 Dart 代码更简洁优雅
在 Dart 和 Flutter 开发中,DRY(Don't Repeat Yourself) 原则是编写清洁、可维护代码的核心。重复的代码不仅增加维护成本,还容易引发 bug。本文将总结 DRY 原则的通用方法论,提炼出在 Dart 中实现 DRY 的“套路”,并通过一个任务管理示例,循序渐进地展示如何应用这些方法,让你的代码更简洁、优雅!
什么是 DRY 原则?
DRY 原则要求避免代码重复,将重复的逻辑抽取为可复用的模块。每次你发现自己在复制粘贴代码时,可能就违反了 DRY。以下是实现 DRY 的方法论,帮你在 Dart 中系统性地消除重复。
DRY 方法论:5 个实用套路
1. 识别重复:找到代码中的“复制粘贴”
重复的代码通常表现为相同的逻辑出现在多个地方。常见场景包括:
- 多个类有相同的属性或方法(如时间戳、ID 生成)。
- 重复的格式化或计算逻辑。
- 相似的业务逻辑分散在不同模块。
套路:在编码前,检查是否有重复的属性、方法或流程,用注释或伪代码标记出来。
2. 抽取函数:封装小的重复逻辑
如果多处代码使用了相同的计算或格式化逻辑,将其抽取为独立函数。
套路:
- 找到重复的代码片段(如字符串拼接、数学计算)。
- 封装成命名清晰的函数,参数化差异部分。
- 确保函数单一职责,易于测试和复用。
3. 使用 Mixin:复用通用行为
当多个类共享相同的属性或方法,但不适合继承时,使用 Mixin。
套路:
- 识别跨类的通用逻辑(如时间戳、日志记录)。
- 创建 Mixin,定义共享的属性和方法。
- 在需要的地方通过
with
关键字复用。
4. 抽象类/接口:统一相似对象的结构
如果多个类有相似的属性和方法,但实现细节不同,使用抽象类或接口。
套路:
- 定义抽象类,声明公共属性和方法。
- 在子类中实现具体差异。
- 确保抽象类只包含必要的共性逻辑。
5. 集中管理:统一操作逻辑
将分散的操作逻辑(如增删改查)集中到一个管理类中,避免在多处重复实现。
套路:
- 创建管理类,负责操作共享数据或对象。
- 使用多态支持不同类型的对象。
- 提供统一的接口,隐藏实现细节。
实战:用 DRY 方法论优化任务管理代码
让我们通过一个任务管理应用,展示如何应用这些套路,从重复的代码到 DRY 的实现。
非 DRY 的代码:问题暴露
假设我们需要管理普通任务和优先级任务:
class Task {
String id;
String title;
bool isCompleted;
DateTime createdAt;
Task(this.title, {this.isCompleted = false})
: id = const Uuid().v4(),
createdAt = DateTime.now();
}
class PriorityTask {
String id;
String title;
bool isCompleted;
DateTime createdAt;
int priority;
PriorityTask(this.title, this.priority, {this.isCompleted = false})
: id = const Uuid().v4(),
createdAt = DateTime.now();
}
问题:
id
、title
、isCompleted
、createdAt
和构造逻辑重复。- 如果需要添加时间戳或新任务类型,需重复修改。
应用 DRY 方法论
套路 1:识别重复
我们发现 Task
和 PriorityTask
共享 id
、title
、isCompleted
和 createdAt
,构造逻辑也几乎相同。
套路 2:抽取函数(示例:格式化逻辑)
假设我们需要格式化任务信息,原本可能在多处写重复的字符串拼接:
// 非 DRY
String taskInfo = '${task.title} [${task.isCompleted ? 'Done' : 'Pending'}]';
String priorityTaskInfo = '${priorityTask.title} [${priorityTask.isCompleted ? 'Done' : 'Pending'}, Priority: ${priorityTask.priority}]';
优化为单一函数:
String formatTaskSummary(dynamic task) {
String base = '${task.title} [${task.isCompleted ? 'Done' : 'Pending'}]';
return task is PriorityTask ? '$base, Priority: ${task.priority}' : base;
}
效果:一个函数适配所有任务类型,减少重复。
套路 3:使用 Mixin 抽取时间戳
时间戳逻辑可以抽取为 Mixin:
mixin Timestampable {
DateTime createdAt = DateTime.now();
DateTime? updatedAt;
void updateTimestamp() {
updatedAt = DateTime.now();
}
}
效果:任何类都可以通过 with Timestampable
复用时间戳逻辑。
套路 4:抽象类统一任务结构
用抽象类 TaskBase
统一公共属性和方法:
abstract class TaskBase with Timestampable {
final String id;
String title;
bool isCompleted;
TaskBase({
required this.title,
this.isCompleted = false,
}) : id = const Uuid().v4();
void toggleCompletion() {
isCompleted = !isCompleted;
updateTimestamp();
}
Map<String, dynamic> toJson();
}
class Task extends TaskBase {
Task({required super.title, super.isCompleted});
@override
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'isCompleted': isCompleted,
'createdAt': createdAt.toIso8601String(),
};
}
class PriorityTask extends TaskBase {
final int priority;
PriorityTask({required super.title, required this.priority, super.isCompleted});
@override
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'isCompleted': isCompleted,
'priority': priority,
'createdAt': createdAt.toIso8601String(),
};
}
效果:TaskBase
消除了属性和方法的重复,子类只实现差异化逻辑。
套路 5:集中管理任务
用单一的 TaskManager
管理所有任务:
class TaskManager {
final List<TaskBase> _tasks = [];
void addTask(String title, {int? priority}) {
_tasks.add(priority == null ? Task(title: title) : PriorityTask(title: title, priority: priority));
}
void toggleTaskCompletion(String id) {
final task = _tasks.firstWhere((task) => task.id == id);
task.toggleCompletion();
}
List<TaskBase> getTasks() => List.unmodifiable(_tasks);
}
效果:统一管理不同任务类型,新增类型无需修改 TaskManager
。
运行示例
import 'package:uuid/uuid.dart';
void main() {
final taskManager = TaskManager();
taskManager.addTask('买菜');
taskManager.addTask('完成项目', priority: 1);
taskManager.toggleTaskCompletion(taskManager.getTasks()[0].id);
for (var task in taskManager.getTasks()) {
print(formatTaskSummary(task));
}
}
输出:
买菜 [Done]
完成项目 [Pending, Priority: 1]
DRY 方法论总结
通过以上套路,我们将非 DRY 的代码优化为简洁、可维护的实现:
方面 | 非 DRY 实现 | DRY 实现 |
---|---|---|
代码量 | 重复属性和方法,代码冗长 | 减少约 30% 代码量 |
维护性 | 多处修改,易出错 | 集中修改 Mixin 或基类 |
扩展性 | 新类型需复制粘贴 | 继承 TaskBase 即可 |
DRY 套路清单:
- 识别重复:检查复制粘贴的代码,标记共性。
- 抽取函数:封装小块逻辑,参数化差异。
- 使用 Mixin:复用跨类的行为。
- 抽象类/接口:统一相似对象的结构。
- 集中管理:用单一类管理操作逻辑。
在 Flutter 开发中,试着将这些套路应用到你的代码中,比如抽取 Widget 的样式逻辑、复用网络请求代码等,你的开发效率和代码质量将大幅提升!
想学习更多 Dart/Flutter 开发技巧?快关注我们微信公众号: OldBirds,获取最新技术干货!