在 Dart 中更好地使用类和 mixin

2021-11-28 20:43:32 浏览数 (1)

Dart 是一门“纯”面向对象的编程语言,其中所有的对象都是类的实例。但是 Dart 并不要求所有代码都定义在一个类中。我们可以在一个类的外面定义顶级变量、常量、函数 —— 就像面向过程语言那样。正式因为这样,Dart 的编码会有些特殊的建议。

建议1:如果一个抽象类只有一个函数,那么直接定义函数会更好

假设我们需要一个回调函数或使用一个函数,在像 Java 那样的语言中你需要定义一个类。但是,在 Dart 中,如果仅仅是一个函数,定义类反而使得代码不好维护。这个时候建议直接使用 typedef 来定义函数别名。

代码语言:javascript复制
// 正确示例
typedef Predicate<E> = bool Function(E element);

// 错误示例
abstract class Predicate<E> {
  bool test(E element);
}
复制代码

建议2:如果一个类只有静态变量或函数的话,那么直接定义顶级的常量和函数来替换这种方式

Java 或 C#中,如果要定义常量的话通常需要定义一个静态常量类来做,例如:

代码语言:javascript复制
// Java 代码
public class ConstParams {
    public static int maxLength = 256;
    public static int minLength = 5;
}
复制代码

这样做的好处是假设静态常量名在多个类中定义的话,可以通过命名空间避免冲突。那么对于 Dart 而言,在类外面定义的变量、函数可以使用库(library)作为命名空间来区分,因此这样的话即便出现变量名一致也不会冲突。

代码语言:javascript复制
// const_params.dart
const int maxLength = 256;
const int minLength = 5;

// test.dart
import 'const_params.dart' as ConstParams;

void main() {
   print('max: ${ConstParams.maxLength}');
}
复制代码

因此在 Dart中,下面的写法是不推荐的。

代码语言:javascript复制
class ConstParams {
  static const maxLength = 256;
  static const minLength = 256;
}
复制代码

当然,这并不是一个硬性规定,比如如果有一组相同类的常量的话,那么使用类会使得代码的可读性更高,例如下面颜色这个例子。

代码语言:javascript复制
class Color {
  static const red = '#f00';
  static const green = '#0f0';
  static const blue = '#00f';
  static const black = '#000';
  static const white = '#fff';
}
复制代码

建议3:不要轻易使用继承

这个在很多语言都有介绍过,继承应该仅在子类符合“is a”父类的关系的时候才使用。比如 Dog 类可以继承 Animal 类,但是这个也应该限于父类足够抽象,没有太多个性化特征,而且未来的改动也极少。

使用继承确实可以减少编码,但是基类的任何变动都可能导致你的子类代码异常。如果你的子类很多的话,那么维护起来是相当恐怖的。一个比较好的建议是,对于支持继承的基类统一命名表名该类可以继承,比如 IterableBase。同时,对于基类一定写好文档,方便想继承该类的子类清楚知道可能的影响。

建议4:不要使用 implements 实现非接口类

接口类的定义的好处是可以在多种实现方式中切换而无需更改代码,在依赖注入型的框架或代码结构中会经常使用面向接口编程的方式。关于依赖注入的文章,可以看之前写的一篇:从创业公司CEO找程序员来说依赖注入。 如果一个类的设计目的不是用作接口的,那么使用 implements 来实现这个类的方法的话是很奇怪的行为。往这个类中加入成员变量不会产生什么问题,但是如果新增方法的话就会意味着代码会出错。因此,如果要采取面向接口编程,定义的接口类应该是一个“虚”类,只有必要方法声明,而没有其他属性。同时,这个类应该有良好的文档注释,以便实现类能够知道如何准确地实现对应的接口。

建议5:优先使用 mixin 关键字定义 mixin 类型

在 Dart 2.1.0版本以前,并没有关键字 mixin,需要混入其他特性的话,需要使用 class 定义混入类型。而在 Dart 2.1.0版本后,引入了 mixin 专门定义混入类型,这样使得语义更加清晰,可以对比一下下面的两种实现方式。很显然,使用 mixin 会让我们更清晰地知道这是一个混入类型,而不会当做一个类来使用。官方的建议是,自 Dart 2.1.0版本以后,不应该再使用 class 关键字定义混入类型,以避免出现随意混入,导致代码不好维护。

代码语言:javascript复制
// mixin 方式
mixin SpeakEnglishMixin {
  void speakEnglish() {
    print('I can speak English fluently');
  }
}

// class 方式
class SpeakEnglishMixin {
  void speakEnglish() {
    print('I can speak English fluently');
  }
}
复制代码

此外,mixin 也可以使用 on 关键字指定只适用于特定的类,从而限定范围,避免滥用。 下面声明的这个例子中 SpeakEnglishMixin 只能用于 Person 子类混入。

代码语言:javascript复制
mixin SpeakEnglishMixin on Person {
  void speakEnglish() {
    print('I can speak English fluently');
  }
}
复制代码

总结

本篇介绍了 Dart 相比其他面向对象语言的一些独有特性,例如类外面的变量、函数定义,mixin 等。其实 Dart 可以说是综合了面向对象语言和动态语言的优点,更贴近现代化编程语言的特性。但编码工作万变不离其宗,我们利用 Dart 的特性应该是为了编写更好维护的代码,而不是滥用误用其新特性。

0 人点赞