类的定义
基本语法
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')}');
}
注意事项
- 类名使用大驼峰命名法
- 私有成员以下划线开头
- 合理使用 Getter 和 Setter
- 避免过度使用静态成员
- 注意构造函数的使用场景
最佳实践
- 遵循单一职责原则
- 使用工厂构造函数创建单例
- 合理使用抽象类和接口
- 适当封装私有成员
- 提供必要的文档注释
实战案例:两个小模型类撑起一个桌面应用
我的 flutter_brightness 项目里,lib/models/ 下只有两个模型类,但整个应用的数据流转都靠它们:
LuxData:光照度数据模型,存传感器读到的光照值。从串口来的原始字符串,解析成LuxData之后,后面所有逻辑(UI 显示、亮度算法、入库)都不再碰原始字符串。CalibrationPoint:校准点数据模型,存”光照度—亮度”对应关系。用户手动调一次亮度,App 就记一个校准点,BrightnessMapping算法靠这些点来拟合曲线。
这两个类都很小,但它们体现了一个关键设计:模型类是”脏数据”和”业务逻辑”之间的防火墙。串口吐出来的是原始文本,LuxData 把它变成有类型的、有意义的数据。后面如果传感器换了协议(比如从文本改成二进制),我只需要改 LuxData 的解析,UI 和算法一行不用动。
新手写模型类常犯的毛病是”贫血”——类里只有字段,所有逻辑都堆在 service 里。我的做法是:LuxData 只负责”是什么”(数据+解析),BrightnessMapping 负责”怎么算”(算法)。模型管数据,服务管行为,分得清清楚楚。类不在多,在职责单一。
总结
Dart 的面向对象没有太多魔法:构造函数的几种写法、私有靠下划线、单例靠工厂,记住这三板斧,配合空安全的初始化规则,日常开发基本够用。类设计上少想点”复用”,多想点”职责单一”,代码自然干净。
