Dart 是一门“纯”面向对象的编程语言,其中所有的对象都是类的实例。但是 Dart 并不要求所有代码都定义在一个类中。我们可以在一个类的外面定义顶级变量、常量、函数 —— 就像面向过程语言那样。正式因为这样,Dart 的编码会有些特殊的建议。
建议1:如果一个抽象类只有一个函数,那么直接定义函数会更好
假设我们需要一个回调函数或使用一个函数,在像 Java 那样的语言中你需要定义一个类。但是,在 Dart 中,如果仅仅是一个函数,定义类反而使得代码不好维护。这个时候建议直接使用 typedef
来定义函数别名。
// 正确示例
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 关键字定义混入类型,以避免出现随意混入,导致代码不好维护。
// 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
子类混入。
mixin SpeakEnglishMixin on Person {
void speakEnglish() {
print('I can speak English fluently');
}
}
复制代码
总结
本篇介绍了 Dart 相比其他面向对象语言的一些独有特性,例如类外面的变量、函数定义,mixin 等。其实 Dart 可以说是综合了面向对象语言和动态语言的优点,更贴近现代化编程语言的特性。但编码工作万变不离其宗,我们利用 Dart 的特性应该是为了编写更好维护的代码,而不是滥用误用其新特性。