跳转到正文
YGJoon 技术博客

Dart 类和对象详解

发布于:
·1.3k 字 · 约 3 分钟·作者:
默认、命名、初始化列表、工厂四种构造函数逐个拆解,配上私有成员与静态成员,Dart 面向对象这块一次讲透。
Dart 类和对象详解文章封面
目录

类的定义

基本语法

class Person {
  // 属性
  String name;
  int age;
  
  // 构造函数
  Person(this.name, this.age);
  
  // 方法
  void sayHello() {
    print('Hello, I am $name');
  }
}

私有成员

class Person {
  String _name; // 私有属性
  int _age;     // 私有属性

  // 非空字段必须在构造时初始化(空安全要求),不能只留 getter/setter
  Person(this._name, this._age);

  // Getter
  String get name => _name;
  int get age => _age;
  
  // Setter
  set name(String value) => _name = value;
  set age(int value) {
    if (value >= 0) {
      _age = value;
    }
  }
}

构造函数

默认构造函数

class Point {
  double x, y;
  
  Point(this.x, this.y);
}

命名构造函数

class Point {
  double x, y;
  
  Point(this.x, this.y);
  
  // 命名构造函数
  // 注意:非空字段不能在构造体里赋值,必须走初始化列表,否则编译报错
  Point.origin()
      : x = 0,
        y = 0;

  Point.fromJson(Map<String, double> json)
      : x = json['x']!,
        y = json['y']!;
}

初始化列表

class Point {
  final double x;
  final double y;
  
  Point(double x, double y)
      : x = x,
        y = y {
    print('Point created');
  }
}

工厂构造函数

class Logger {
  static final Map<String, Logger> _cache = {};
  
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }
  
  Logger._internal(String name);
}

静态成员

静态属性

class MathUtils {
  static const double pi = 3.14159;
  static int count = 0;
}

静态方法

class MathUtils {
  static double square(double num) {
    return num * num;
  }
  
  static int add(int a, int b) => a + b;
}

Getter 和 Setter

class Rectangle {
  double _width;
  double _height;
  
  Rectangle(this._width, this._height);
  
  // Getter
  double get area => _width * _height;
  
  // Setter
  set width(double value) {
    if (value > 0) {
      _width = value;
    }
  }
  
  set height(double value) {
    if (value > 0) {
      _height = value;
    }
  }
}

抽象类和方法

abstract class Shape {
  // 抽象方法
  double getArea();
  double getPerimeter();
  
  // 具体方法
  void printInfo() {
    print('Area: ${getArea()}');
    print('Perimeter: ${getPerimeter()}');
  }
}

class Circle extends Shape {
  double radius;
  
  Circle(this.radius);
  
  @override
  double getArea() => 3.14 * radius * radius;
  
  @override
  double getPerimeter() => 2 * 3.14 * radius;
}

完整示例

class BankAccount {
  // 私有属性
  String _accountNumber;
  double _balance;
  static const double _minBalance = 100.0;
  
  // 构造函数
  BankAccount(this._accountNumber, [this._balance = 0.0]);
  
  // 命名构造函数
  BankAccount.withBalance(String accountNumber, double balance)
      : _accountNumber = accountNumber,
        _balance = balance {
    print('Account created with initial balance');
  }
  
  // Getter
  String get accountNumber => _accountNumber;
  double get balance => _balance;
  
  // 方法
  bool deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
      return true;
    }
    return false;
  }
  
  bool withdraw(double amount) {
    if (amount > 0 && (_balance - amount) >= _minBalance) {
      _balance -= amount;
      return true;
    }
    return false;
  }
  
  // 静态方法
  static bool validateAccountNumber(String accountNumber) {
    return accountNumber.length == 10;
  }
  
  // 重写 toString 方法
  @override
  String toString() {
    return 'Account: $_accountNumber, Balance: \$$_balance';
  }
}

void main() {
  // 创建账户
  var account1 = BankAccount('1234567890');
  var account2 = BankAccount.withBalance('0987654321', 1000.0);
  
  // 存款
  account1.deposit(500.0);
  print(account1);
  
  // 取款
  if (account2.withdraw(300.0)) {
    print('Withdrawal successful');
    print(account2);
  } else {
    print('Withdrawal failed');
  }
  
  // 验证账号
  print('Account number valid: ${BankAccount.validateAccountNumber('1234567890')}');
}

注意事项

  1. 类名使用大驼峰命名法
  2. 私有成员以下划线开头
  3. 合理使用 Getter 和 Setter
  4. 避免过度使用静态成员
  5. 注意构造函数的使用场景

最佳实践

  1. 遵循单一职责原则
  2. 使用工厂构造函数创建单例
  3. 合理使用抽象类和接口
  4. 适当封装私有成员
  5. 提供必要的文档注释

实战案例:两个小模型类撑起一个桌面应用

我的 flutter_brightness 项目里,lib/models/ 下只有两个模型类,但整个应用的数据流转都靠它们:

这两个类都很小,但它们体现了一个关键设计:模型类是”脏数据”和”业务逻辑”之间的防火墙。串口吐出来的是原始文本,LuxData 把它变成有类型的、有意义的数据。后面如果传感器换了协议(比如从文本改成二进制),我只需要改 LuxData 的解析,UI 和算法一行不用动。

新手写模型类常犯的毛病是”贫血”——类里只有字段,所有逻辑都堆在 service 里。我的做法是:LuxData 只负责”是什么”(数据+解析),BrightnessMapping 负责”怎么算”(算法)。模型管数据,服务管行为,分得清清楚楚。类不在多,在职责单一。

总结

Dart 的面向对象没有太多魔法:构造函数的几种写法、私有靠下划线、单例靠工厂,记住这三板斧,配合空安全的初始化规则,日常开发基本够用。类设计上少想点”复用”,多想点”职责单一”,代码自然干净。


分享这篇文章:


相关文章

上一篇
Dart 函数和方法详解
下一篇
Dart 代码生成详解