[英]Dart factory constructor - how is it different to “const” constructor
[英]Understanding Factory constructor code example - Dart
我對此處提到的工廠構造函數示例有一些瑣碎的問題( https://www.dartlang.org/guides/language/language-tour#factory-constructors )。 我只知道基本級別的三種構造函數——默認、命名和參數化。
tl;dr在您不一定要返回類本身的新實例的情況下使用工廠。 用例:
解釋
Dart 類可能具有生成構造函數或工廠構造函數。 生成構造函數是一個總是返回類的新實例的函數。 因此,它不使用return
關鍵字。 常見的生成構造函數具有以下形式:
class Person {
String name;
String country;
// unnamed generative constructor
Person(this.name, this.country);
}
var p = Person("...") // returns a new instance of the Person class
工廠構造函數比生成構造函數具有更寬松的約束。 工廠只需要返回一個與該類類型相同或實現其方法(即滿足其接口)的實例。 這可以是該類的新實例,但也可以是該類的現有實例或子類的新/現有實例(必須具有與父類相同的方法)。 工廠可以使用控制流來確定返回什么對象,並且必須使用return
關鍵字。 為了讓工廠返回一個新的類實例,它必須首先調用生成構造函數。
在您的示例中,未命名的工廠構造函數首先從名為_cache
的 Map 屬性中讀取(由於它是Static
,因此存儲在類級別,因此獨立於任何實例變量)。 如果實例變量已經存在,則返回它。 否則,將通過調用命名的生成構造函數Logger._internal
生成一個新實例。 該值被緩存然后返回。 因為生成構造函數只接受一個name
參數,所以mute
屬性將始終初始化為 false,但可以使用默認設置器進行更改:
var log = Logger("...");
log.mute = true;
log.log(...); // will not print
factory
一詞指的是工廠模式,即允許構造函數根據提供的參數返回子類實例(而不是類實例)。 Dart 中這個用例的一個很好的例子是抽象的 HTML Element 類,它定義了幾十個返回不同子類的命名工廠構造函數。 例如, Element.div()
和Element.li()
分別返回<div>
和<li>
元素。
在這個緩存應用程序中,我發現“工廠”有點用詞不當,因為它的目的是避免調用生成構造函數,而且我認為現實世界的工廠本質上是生成的。 也許這里更合適的術語是“倉庫”:如果某件商品已經可用,請將其從貨架上取下並交付。 如果沒有,請調用一個新的。
所有這些與命名構造函數有何關系? 生成構造函數和工廠構造函數都可以是未命名的或已命名的:
...
// named generative
// delegates to the default generative constructor
Person.greek(String name) : this(name, "Greece");
// named factory
factory Person.greek(String name) {
return Greek(name);
}
}
class Greek extends Person {
Greek(String name) : super(name, "Greece");
}
靜態方法和工廠構造函數之間沒有太大區別。
對於工廠構造函數,返回類型固定為類的類型,而對於靜態方法,您可以提供自己的返回類型。
可以使用new
調用工廠構造函數,但這與 Dart 2 中的可選new
幾乎無關。
還有其他一些功能,例如很少使用的重定向,它們受(工廠)構造函數支持,但不支持靜態方法。
使用工廠構造函數而不是靜態方法來創建類的實例可能仍然是一個好習慣,以使創建對象的目的更加明顯。
這就是在您發布的示例中使用工廠構造函數的原因,也許是因為代碼最初是在 Dart 1 中編寫的,它允許像任何其他類一樣使用new
創建一個記錄器實例。
是的,這是一個命名構造函數,前綴_
使其成為私有命名構造函數。 只有命名的構造函數可以設為私有,否則就沒有地方可以添加_
前綴。
它用於防止從公共工廠構造函數以外的任何地方創建實例。 這樣可以確保您的應用程序中不會有多個Logger
實例。 工廠構造函數只在第一次創建一個實例,后續調用總是返回先前創建的實例。
作為對 Dave 答案的補充,此代碼顯示了使用工廠返回父相關類時的清晰示例。
從https://codelabs.developers.google.com/codelabs/from-java-to-dart/#3查看這段代碼
您可以在此處運行此代碼。 https://dartpad.dartlang.org/63e040a3fd682e191af40f6427eaf9ef
進行一些更改以了解它在某些情況下的工作方式,例如單身人士。
import 'dart:math';
abstract class Shape {
factory Shape(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
// To trigger exception, don't implement a check for 'triangle'.
throw 'Can\'t create $type.';
}
num get area;
}
class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}
class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}
class Triangle implements Shape {
final num side;
Triangle(this.side);
num get area => pow(side, 2) / 2;
}
main() {
try {
print(Shape('circle').area);
print(Shape('square').area);
print(Shape('triangle').area);
} catch (err) {
print(err);
}
}
除了其他答案之外,還要考慮實例化對象的順序以及創建實例的時間:
在普通構造函數中,創建一個實例,並使用初始化列表實例化最終變量。 這就是為什么沒有return
語句的原因。 執行構造函數時,要返回的實例已經固定!
在工廠構造函數中,要返回的實例由方法決定。 這就是為什么它需要一個return
語句以及為什么它通常會在至少一個路徑中調用普通構造函數的原因。
因此,工廠所做的與靜態方法所做的沒有什么不同(在其他語言中通常稱為getInstance()
),除了您不能使用靜態方法重載構造函數但可以使用工廠方法。 即工廠方法是一種隱藏類的用戶不是調用構造函數而是調用靜態方法這一事實的方法:
// C++
MyCoolService.getInstance()
// Dart
MyCoolService()
我對這里提到的工廠構造函數示例有些疑問( https://www.dartlang.org/guides/language/language-tour#factory-constructors )。 在基本級別上,我只知道三種類型的構造函數-默認,命名和參數化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.