[TOC]
在Dart中,对象具有成员,成员可以是函数(方法)或数据(实例变量)。以下最佳实践适用于对象的成员。
## 不要把不必要地将字段包装在getter和setter中。
在Java和c#中,通常将所有字段隐藏在getter和setter之后(或c#中的属性),即使实现只是转发给字段。这样,如果您需要在这些成员中做更多的工作,您可以不需要接触callsites。这是因为调用getter方法与访问Java中的字段不同,访问属性与访问c#中的原始字段不兼容。
Dart没有这个限制。字段和getter /setter完全无法区分。您可以在类中公开一个字段,然后将其包装在getter和setter中,而不必接触任何使用该字段的代码。
~~~
class Box {
var contents;
}
~~~
以下是不推荐的写法:
~~~
class Box {
var _contents;
get contents => _contents;
set contents(value) {
_contents = value;
}
}
~~~
## 优先使用final字段来创建只读属性。
如果您有一个字段,外部代码应该能够看到,但不能分配给它,一个简单的解决方案是简单地标记它为final。
~~~
class Box {
final contents = [];
}
~~~
以下是不推荐的写法:
~~~
class Box {
var _contents;
get contents => _contents;
}
~~~
当然,如果您需要在构造函数外部内部分配字段,那么您可能需要执行“私有字段、公共getter”模式,但是在需要之前不要这样做。
## 考虑对简单成员使用=>。
除了对函数表达式使用=>外,Dart还允许使用它定义成员。这种样式非常适合于只计算并返回值的简单成员。
~~~
double get area => (right - left) * (bottom - top);
bool isReady(num time) => minTime == null || minTime <= time;
String capitalize(String name) =>
'${name[0].toUpperCase()}${name.substring(1)}';
~~~
写代码的人似乎喜欢=>,但很容易滥用它,最终导致难以阅读的代码。如果您的声明超过几行或包含深度嵌套的表达式(级联和条件运算符是常见的攻击者),请您和所有需要阅读您的代码的人帮忙,并使用块体和一些语句。
~~~
Treasure openChest(Chest chest, Point where) {
if (_opened.containsKey(chest)) return null;
var treasure = Treasure(where);
treasure.addAll(chest.contents);
_opened[chest] = treasure;
return treasure;
}
~~~
以下是不推荐的写法:
~~~
Treasure openChest(Chest chest, Point where) =>
_opened.containsKey(chest) ? null : _opened[chest] = Treasure(where)
..addAll(chest.contents);
~~~
对于不返回值的成员,也可以使用=>。如果setter很小,并且具有使用=>的相应getter,那么这就是惯用方法。
~~~
num get x => center.x;
set x(num value) => center = Point(value, center.y);
~~~
对于非setter void成员,使用=>不是一个好主意。=>意味着“返回一个值”,因此如果您使用void成员,读者可能会误解它的作用。
## 在不需要的时候不要用this.以免产生歧义。
JavaScript需要明确这一点。引用当前正在执行其方法的对象上的成员,但是像dart一样的c++、Java和c#没有这个限制。
你唯一需要使用这个的场景是:当具有相同名称的局部变量隐藏您想要访问的成员时。
以下是不推荐的写法:
~~~
class Box {
var value;
void clear() {
this.update(null);
}
void update(value) {
this.value = value;
}
}
~~~
以下是推荐的写法:
~~~
class Box {
var value;
void clear() {
update(null);
}
void update(value) {
this.value = value;
}
}
~~~
注意,构造函数参数从不在构造函数初始化列表中隐藏字段:
~~~
class Box extends BaseBox {
var value;
Box(value)
: value = value,
super(value);
}
~~~
这看起来令人惊讶,但工作起来像你想要的。幸运的是,由于初始化了formals,这样的代码相对少见。
## 在可能的情况下,对字段的声明进行初始化。
如果字段不依赖于任何构造函数参数,则可以并且应该在声明时初始化它。它需要更少的代码,并且确保如果类有多个构造函数,您不会忘记初始化它。
以下是不推荐的写法:
~~~
class Folder {
final String name;
final List<Document> contents;
Folder(this.name) : contents = [];
Folder.temp() : name = 'temporary'; // Oops! Forgot contents.
}
~~~
以下是推荐的写法:
~~~
class Folder {
final String name;
final List<Document> contents = [];
Folder(this.name);
Folder.temp() : name = 'temporary';
}
~~~
当然,如果字段依赖于构造函数参数,或者由不同的构造函数以不同的方式初始化,那么这个指导原则就不适用。