请理解不可变性和正确使用Freezed

Immutability 不可变性,在代码中,我们通常使用final 声明一个值被声明之后不能被改变,也就是只有get方法,没有set 方法。这在很多场景下很有用,但这也不是银弹。我们需要了解不可变性的适用范围,否则freezed的滥用也会导致我们的代码变得冗余晦涩

不可变性是一种设计模式,他关注的一个对象被创建后不可修改(包括内部的变量),这点很类似于RustCopy语意,也就是值拷贝,对对象的修改,是通过产生新的对象,保持,也类似于集合当中的CopyOnWirte, 他们都保证了数据一致性

不可变性和Bloc, 或者说,一种状态管理模式很搭配,当我们使用一个Class而不是基础数据结构做状态时,我们必须重写 hashCode== 来保证当内部状态被改变时,产生了状态不一致,进而需要通知页面修改最新的状态。这并不是一个容易的过程,但优势是显而易见的。

  • 易于推理和测试: 状态的修改是可以在类的级别进行记录,而不需要对没有属性进行记录。且对象不会在运行时改变,对测试更友好。
  • 无副作用函数式编程: 在非纯函数编程的语言中,我们都会写出产生副作用的代码,对象的不可变性,避免了这种副作用的产生。

常见的正确用法

数据传输对象(DTO)

当你需要定义用于数据传输的简单数据类时,使用 freezed 可以方便地创建不可变的 DTO,确保数据的一致性和不变性。

state 状态管理
  • 单一职责,状态的定义清晰,
  • 状态改变将产生新的状态,状态转移可控。
dart 复制代码
@freezed
class SignInState with _$SignInState {
  const factory SignInState({
    String? email,
    String? password,
    required bool isSubmitting,
    required Option<String> passwordError,
    required Option<String> emailError,
    required Option<Either<UserProfile, PixError>> successOrFail,
  }) = _SignInState;

  factory SignInState.initial() => SignInState(
        isSubmitting: false,
        passwordError: none(),
        emailError: none(),
        successOrFail: none(),
      );
}
事件定义
  • 不可变性也非常适合,Bloc中事件的定义,一旦事件被产生,中途不可被修改,不必担心事件中途被修改
dart 复制代码
@freezed
class SignInEvent with _$SignInEvent {
  const factory SignInEvent.signedInWithUserEmailAndPassword() =
      SignedInWithUserEmailAndPassword;
  const factory SignInEvent.emailChanged(String email) = EmailChanged;
  const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
}

不太友好的使用

在DDD中领域Model中,我们需要对模型对象的值修改,或者执行某些操作需要改变内部值,但不可变性的特质使我们无法在内部完成这件事,我们必须将内部变化的逻辑放到外部,降低了内聚性。

dart 复制代码
@freezed
@HiveType(typeId: 4)
class EffectiveRange with _$EffectiveRange {
  factory EffectiveRange({
    @HiveField(0) required int start,
    @HiveField(1) required int end,
  }) = _EffectiveRange;
}

由于内部并不能添加修改相关的方法,无法继承Hive Object,进行操作。我们也无法提供其他方法优化对Model的访问。但在dart中有一种读取的思路是使用 extension

dart 复制代码
extension EffectiveRangeExtension on EffectiveRange {
  String get labelStart => start.kTimeLabel;
  String get labelEnd => end.kTimeLabel;

  String get label => "$labelStart -$labelEnd";
}

不可变性freezed的关系是,理论和实现方式的,不可变性在实际项目使用当中要进行一定的权衡,它并不是 银弹。但我们可以通过freezed 生成的方法,改变为部分可变性,这在某些场景下也非常的合适。使用的方式和范围要看自己实际的项目需求了。

Trade Off 需要实际使用过两种甚至多种方式,并发现优势和不足,才能做的,多做,多写,多深入,才能做好这份工作。

相关推荐
Summer不秃2 小时前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰2 小时前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
sunly_2 小时前
Flutter:AnimatedSwitcher当子元素改变时,触发动画
flutter
AiFlutter2 小时前
Flutter封装Coap
flutter
旭日猎鹰8 小时前
Flutter踩坑记录(三)-- 更改入口执行文件
flutter
旭日猎鹰8 小时前
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
flutter
️ 邪神8 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
比格丽巴格丽抱20 小时前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart20 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
AiFlutter1 天前
Flutter通过 Coap发送组播
flutter