DRY 原则方法论:如何让 Dart 代码更简洁优雅

39 阅读5分钟

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

问题

  • idtitleisCompletedcreatedAt 和构造逻辑重复。
  • 如果需要添加时间戳或新任务类型,需重复修改。

应用 DRY 方法论

套路 1:识别重复

我们发现 TaskPriorityTask 共享 idtitleisCompletedcreatedAt,构造逻辑也几乎相同。

套路 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 套路清单

  1. 识别重复:检查复制粘贴的代码,标记共性。
  2. 抽取函数:封装小块逻辑,参数化差异。
  3. 使用 Mixin:复用跨类的行为。
  4. 抽象类/接口:统一相似对象的结构。
  5. 集中管理:用单一类管理操作逻辑。

在 Flutter 开发中,试着将这些套路应用到你的代码中,比如抽取 Widget 的样式逻辑、复用网络请求代码等,你的开发效率和代码质量将大幅提升!

想学习更多 Dart/Flutter 开发技巧?快关注我们微信公众号: OldBirds,获取最新技术干货!