Skip to content

Dart Mixin 和扩展详解

Mixin 基础

Mixin 声明

dart
mixin Logger {
  void log(String message) {
    print('Log: $message');
  }
  
  void error(String message) {
    print('Error: $message');
  }
}

mixin TimeStamp {
  String getTimestamp() {
    return DateTime.now().toIso8601String();
  }
}

Mixin 使用

dart
class Service with Logger, TimeStamp {
  void doSomething() {
    log('Operation started at ${getTimestamp()}');
    // 执行操作
    log('Operation completed');
  }
}

Mixin 约束

on 关键字

dart
// 限制只能在特定类型上使用
mixin CanFly on Bird {
  void fly() {
    print('Flying high!');
  }
}

class Bird {
  void chirp() {
    print('Chirp chirp!');
  }
}

class Eagle extends Bird with CanFly {
  // 可以使用 fly 方法
}

class Dog with CanFly {  // 错误: Dog 不是 Bird 的子类
  // 编译错误
}

多个 Mixin

dart
mixin A {
  String getMessage() => 'A';
}

mixin B {
  String getMessage() => 'B';
}

class C with A, B {
  // 使用最后一个 mixin 的实现
  // getMessage() 返回 'B'
}

扩展方法

基本语法

dart
extension StringExtension on String {
  bool isValidEmail() {
    return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this);
  }
  
  String capitalize() {
    if (isEmpty) return this;
    return '${this[0].toUpperCase()}${substring(1)}';
  }
}

// 使用扩展方法
void main() {
  print('test@example.com'.isValidEmail()); // true
  print('hello'.capitalize()); // Hello
}

泛型扩展

dart
extension ListExtension<T> on List<T> {
  T? firstWhereOrNull(bool Function(T) test) {
    for (var element in this) {
      if (test(element)) return element;
    }
    return null;
  }
  
  List<T> sortedBy(Comparable Function(T) keyExtractor) {
    var copy = [...this];
    copy.sort((a, b) => keyExtractor(a).compareTo(keyExtractor(b)));
    return copy;
  }
}

完整示例

dart
// 基础日志 mixin
mixin BaseLogger {
  void log(String message) {
    print('${DateTime.now()}: $message');
  }
}

// 文件操作 mixin
mixin FileOperations {
  Future<void> writeToFile(String path, String content) async {
    log('Writing to file: $path'); // 可以使用 BaseLogger 的方法
    // 文件写入逻辑
  }
  
  Future<String> readFromFile(String path) async {
    log('Reading from file: $path');
    // 文件读取逻辑
    return 'File content';
  }
}

// 数据验证 mixin
mixin DataValidator {
  bool validateEmail(String email) {
    return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
  }
  
  bool validatePhone(String phone) {
    return RegExp(r'^\+?[\d\s-]{10,}$').hasMatch(phone);
  }
}

// 使用多个 mixin
class UserService with BaseLogger, FileOperations, DataValidator {
  Future<void> saveUser(Map<String, String> userData) async {
    // 验证数据
    if (!validateEmail(userData['email']!)) {
      log('Invalid email address');
      return;
    }
    
    if (!validatePhone(userData['phone']!)) {
      log('Invalid phone number');
      return;
    }
    
    // 保存数据
    await writeToFile('users.json', userData.toString());
    log('User data saved successfully');
  }
}

// 扩展方法示例
extension MapExtension<K, V> on Map<K, V> {
  Map<K, V> merge(Map<K, V> other) {
    return {...this, ...other};
  }
  
  V? getPath(List<K> path) {
    var current = this;
    for (var i = 0; i < path.length - 1; i++) {
      if (current[path[i]] is! Map) return null;
      current = current[path[i]] as Map<K, V>;
    }
    return current[path.last];
  }
}

void main() async {
  // 使用 UserService
  final userService = UserService();
  await userService.saveUser({
    'email': 'test@example.com',
    'phone': '+1234567890',
  });
  
  // 使用扩展方法
  final map1 = {'a': 1, 'b': 2};
  final map2 = {'c': 3, 'd': 4};
  final merged = map1.merge(map2);
  print(merged); // {a: 1, b: 2, c: 3, d: 4}
  
  final nested = {
    'user': {
      'address': {
        'city': 'New York'
      }
    }
  };
  print(nested.getPath(['user', 'address', 'city'])); // New York
}

高级用法

条件 Mixin

dart
mixin DebugLogger on Object {
  void debugLog(String message) {
    assert(() {
      print('Debug: $message');
      return true;
    }());
  }
}

扩展运算符

dart
extension IterableExtension<T> on Iterable<T> {
  T? get firstOrNull => isEmpty ? null : first;
  
  Map<K, List<T>> groupBy<K>(K Function(T) keyExtractor) {
    var result = <K, List<T>>{};
    for (var element in this) {
      var key = keyExtractor(element);
      (result[key] ??= []).add(element);
    }
    return result;
  }
}

最佳实践

  1. 合理使用 Mixin 组合功能
  2. 避免 Mixin 之间的冲突
  3. 为扩展方法提供文档
  4. 使用有意义的命名
  5. 注意扩展方法的作用域

注意事项

  1. Mixin 不支持构造函数
  2. 注意 Mixin 的顺序
  3. 避免过度使用扩展方法
  4. 处理好命名冲突
  5. 考虑扩展方法的性能影响

总结

Mixin 和扩展是 Dart 中强大的代码复用机制,通过合理使用这些特性,可以编写出更加模块化、可维护的代码。理解并掌握这些知识对于开发高质量的 Flutter 应用至关重要。