如果你能弄清楚如何重命名这个问题,我愿意接受建议。
在Dart语言中,可以编写一个包含final字段的类。这些字段只能在构造函数主体运行之前设置为。这可以是声明(通常用于类中的静态常量),在声明构造函数或使用this.field
简写时的初始化列表语法中:
class NumBox{
final num value;
NumBox(this.value);
}
假设我实际上需要对实例创建进行一些处理,而不能仅仅在构造函数之前初始化字段。我可以切换到使用带有getter的私有非final字段:
class NumBox{
num _value;
NumBox(num v) {
_value = someComplexOperation(v);
}
num get value => _value;
}
或者我可以使用工厂构造函数获得类似的行为:
class NumBox{
final num value;
factory NumBox(num v) {
return new NumBox._internal(someComplexOperation(v));
};
NumBox._internal(this.value);
}
几年前,当我尝试学习Dart时,我遇到了类似的磕碰,现在我有更多的行李,我仍然不知道。 更聪明的方法是什么?
答案 0 :(得分:4)
工厂构造函数是一种好方法,它允许预先计算任何值,然后传递给普通构造函数转发到最终字段。
另一种方法是初始化列表,它在构造函数体之前执行,因此允许初始化最终字段:
class NumBox{
final num value;
NumBox(num v) : value = someComplexOperation(v)
}
在初始化列表中,您不能阅读this
,因为该实例尚未完全初始化。
答案 1 :(得分:3)
您应该在设计API时考虑用户,然后以更简单,更易于维护的方式实现它。这个问题是关于后半部分的。
在可能的情况下让场地最终是很好的,如果不是,那么将它们变成私人的公共吸气者是一个很好的选择。这是你的选择,因为你是谁来维护你的课程,没有其他人需要看看公共API。
如果您需要复杂的计算,GüntherZöchbauer的建议是第一个转向:使用辅助函数。在某些情况下,您甚至可以内联
class MyThing {
final x;
MyThing(args) : x = (() { complex code on args; return result;} ());
}
但是,它很快变得丑陋,因此将其作为静态辅助函数通常会更好。
如果您的复杂计算与此不匹配,这通常意味着有多个字段使用相关值进行初始化,那么您需要一个存储中间值的位置并在初始化对象时多次使用它。
工厂构造函数是一种简单的方法,您可以计算所需的一切,然后在最后调用私有生成构造函数。唯一的“问题”是,如果不暴露生成构造函数,就会阻止其他人扩展你的类。我引用了“问题”,因为这并不一定是坏事 - 允许人们扩展课程是一个合同,它限制了你可以对课程做什么。 我倾向于倾向于使用私有生成构造函数的公共工厂构造函数,即使在任何实际原因中不需要它,只是为了禁用类扩展。
class MyClass {
const factory MyClass(args) = MyClass._; // Can be const, if you want it.
const MyClass._(args) : ... init .. args ...;
}
如果您需要生成构造函数,您可以使用转发生成构造函数来引入具有计算值的中间变量:
class MyClass {
final This x;
final That y;
MyClass(args) : this._(args, _complexComputation(args));
MyClass._(args, extra) : x = extra.theThis, y = extra.theThat, ...;
}
总而言之,没有严格的规定。如果您更喜欢最终字段,您可以做额外的工作来使它们成为最终字段,或者您可以隐藏getter背后的可变性 - 这是一个实现和可维护性选择,而您就是维护代码的人。 只要你保持抽象清洁,并跟踪你承诺的用户(生成构造函数?const构造函数?)所以你不会破坏它,你可以随时改变实现。