工厂函数:用factory修饰的函数。
首先,我们来看看dart官方文档到底为我们留下了什么。

(译文来自机器翻译)
没错!官档就给我们留下这些东西,两行解释,两个例子,一个注意,然后就没了!
这对于没有其他语言基础的我来说简直是场灾难!
面对一门语言 ,我认为至少需要能看懂官档在说什么,然而遗憾的是,我其实是一脸懵逼的。下面,我将会把我自己对factory的理解完全清楚的表达出来。
注意;观看本文章之前,我希望你能清楚的知道 普通类 抽象类 类中的属性 类中的方法 类中的类名构造函数 和 命名构造到底是什么 静态(static)相关 这些内容我只会一笔带过,重点还是在工厂构造函数上面。
虽然说的是工厂函数,但是我更喜欢称呼我现在要说的内容是-工厂构造函数。因为现在你看到的内容,全都是在类中的。在类中,factory只可用于构造函数,完整对应的应该是工厂类名函数和工厂命名函数。
其他构造函数不能有返回值,也就是不能出现return关键字,但是 工厂函数肯定有,可以是自定义,或者被动定义( return null; ),返回的是一个实例。
工厂构造函数return的返回值类型必须是构造函数(打印其类型结果是当前类名,这里可以先忽略理解①)
factory可以放在类名函数之前,也可以放在命名函数之前;放在类中的其他属性或者方法上,会提示错误。这里我们知道了在类中最多有三样东西:属性、方法和构造函数,其中构造函数包含类名构造函数和命名构造方法,在构造方法前加上factory之后变成了工厂构造函数,这里我们算是理清类类的结构了。
工厂构造函数不能和其他构造函数一样,使用this关键字来使用class的属性和方法。工厂构造函数只能使用类中static类型的属性和方法。
非工厂类中的构造函数可以使用this.的方式调用类中的非静态属性或者方法,大概就是 int xxx; this.xxx=xx;的感觉。工厂构造函数不能使用这些非静态的属性或者方法。
不论是工厂构造函数还是非工厂构造函数,都可以使用静态(static)类型的属性和方法,大概就是 static int b; b=xx;的感觉。
工厂构造函数可以调用其他构造函数;
虽然同为类中的三大基础,但是构造函数前不能有static。但是属性和方法前都可以有static。
好了,写了这么多,时候来点点代码了。
官方文档给出的是一个有关缓存问题的代码,是非常经典的。但是对于我这个没见过缓存啊 寄存器啊长什么样的人来说。理解起来有点困难。为了帮助自己和同样理解艰的人更好的理解这个factory到底能做什么,我为大家带来一段新的代码。这段代码是我们生活中一个常见事件的表达。
事情是这样的,楼下水果店老板Asery每次从批发市场批发来新的水果之后,都需要给新水果贴上一个出售价格,新批发的水果知道是什么名字,但是因为季节原因可能会有新的水果上架,店里没有新水果的价格。Asery需要做的就是,查库存,然后给新品水果贴价格标签。 下面我们用dart语言来描述Asery需要做的工作。
入手:我们发现,整个过程,就是让不知道价格的水果有一个明确的售价,那么,我们处理的对象应该是水果,我们这里建立一个水果(Fruit())类,来处理店铺进货之后水果的售价问题。
class Fruit{///一个平平无奇的Fruit类
///Fruit();///默认类名构造函数,本次示例未出场。
String newFruit;
int newPrice;
///价格和水果名是成对出现的,所以会产生一堆键值对↓
static Map<String, Fruit> aMapPrice = {};///工厂函数需要用到这个map,所以必须是static
///上面这个map的value部分是当前类,如果你对这部分感到困惑,可以这样想↓:
///每一个新来的水果 和 仓库里的水果的关系,新来的水果我们入库前只知道“名字”,而我们仓库里的水果有“名字”还有“价格”
///这里map的key就是新水果名字,和仓库里水果的关系。
///我们需要完成的事情是将新来的水果变成我们仓库里的,毕竟别人问我们自己仓库的水果多少钱一斤,我们自己需要有报价
///
///下面是工厂函数,name是新入库水果的名称,实例这个函数,会有一个全新的或者已经存在的水果,属于Fruit类
factory Fruit.aNew(String name) {///这个工厂函数是生成新标签的,给新来的水果贴价钱
if (aMapPrice.containsKey(name)) {///如果新水果价格已经存在
print('这种水果($name)已经有记录了,不需要再贴价格了');
return aMapPrice[name];///返回一个已存在的实例;
} else {///如果这个名叫 name 的水果没有价格记录
final newTag = Fruit._getPrice(name);///这里调用了一个命名构造函数,并且实例化了
aMapPrice[name] = newTag;
///就结果而言,上面这里完成了为仓库添加了一个新水果(含名字和价格)的操作,计算机理解就是完成了一次map.addAll({ });
return newTag;
///虽然这里返回的是一个新的实例,但是从结果上来看,更像是一次数据的更新,
///让这组新的关系(价格和水果名)的关系“从无到有”。
}
///factory(工厂)构造函数有一个return,也就是返回了一个值,这个值到底是什么呢?
///从类型上看,是返回了一个构造函数。 我认为其实就是返回了一个实例。
///这个实例根据工厂构造函数的变量变化而改变。也就是说,
///如果你在外部通过这个工厂构造函数(变量) 来实例化这个工厂函数,最终产生的实例只和这个 变量 有关,
///因为通过这个变量,我们在构造函数中就创建好一个固定(包含已经设置好了 属性和方法)的类。
}
///下面提供一个方法简单获取新水果的价格
static nameToPrice(String newName){///如果想在工厂构造函数中实例化这个方法(例如:var i=nameToPrice(xx);),必须有static
return newName.length;
}
///根据水果名获取水果价格 并且将新水果记录下来↓
Fruit._getPrice(String nf){///当我们需要获取价格的时候,证明这水果曾经不存在记录。
this.newFruit= nf;///给新水果贴好名字
this.newPrice= nameToPrice(nf);///获取新水果的价格,这里是赋值,没有对方法进行实例化。
}
////下面这部分虽然运行没错,但是似乎不重要,大概就是根据价格选水果,有兴趣的可以取消注释看看
// factory Fruit.choice(String some){
// var i = nameToPrice(some);//这里对方法进行了实例化,虽然返回的是一个int
// if(i>0&&i<10){
// return Fruit.aNew('苹果');
// }else if(i>=10){
// return Fruit.aNew('西瓜');
// }else{
// print('价格有问题');
// return null;
// }
// }
///上面我们知道工厂函数返回的就是一个实例,下面我们来耍点小花招///
/// 上面我们张口闭口都是带有变量的工厂函数,但是工厂函数没有变量也是可以的;
factory Fruit.banana(){
return Fruit.aNew('香蕉');
}
factory Fruit._(){
return null;
}
} void _aboutFactory() {
///定义一个变量新来水果的名称↓
String aNewFruit;
///第一轮↓
aNewFruit='西瓜';
///给水果贴标,实例出来这个新水果↓
var anf= Fruit.aNew(aNewFruit);
print('$aNewFruit 的价格是:${anf.newPrice} 元');//西瓜 的价格是:2 元
///第二轮↓
aNewFruit='苹果';
var ang=Fruit.aNew(aNewFruit); //苹果 的价格是:2 元
print('$aNewFruit 的价格是:${ang.newPrice} 元');
///第三轮↓
aNewFruit='西瓜';
var anv=Fruit.aNew(aNewFruit); //这种水果(西瓜)已经有记录了,不需要再贴价格了
print('$aNewFruit 的价格是:${anv.newPrice} 元');//西瓜 的价格是:2 元
///---------------分割线--------------
identical(anf, anv)?print('anf 和 anv 是同一个Object'):print('anf 和 anv 是两个不同的Object');
//anf 和 anv 是同一个Object
identical(anf, ang)?print('anf 和 ang 是同一个Object'):print('anf 和 ang 是两个不同的Object');
//anf 和 ang 是两个不同的Object
// var choice=Fruit.choice('西岚葡萄水蜜瓜');//这种水果(苹果)已经有记录了,不需要再贴价格了
// print(choice.newPrice);//2
// print(choice.newFruit);//苹果
var bana=Fruit.banana();
var banb=Fruit.banana();//这种水果(香蕉)已经有记录了,不需要再贴价格了
assert(identical(bana, banb));
print(banb.newFruit);//香蕉
}我在执行_aboutFactory()之后的结果如下图。
这里需要单独说一下抽象类中factory的情况。
我们知道,抽象类只能被 继承 或者 当做 接口,不能被实例化。但是抽象类中的工厂构造函数是可以实例化的,但是即便实例化了也没有什么作用,因为我们实例化工厂函数是为了帮我们创建一个新的实体,但是在抽象类中,我们的工厂构造函数常用的也就是在函数体中return null;而不是返回一个实例,这个工厂构造函数的实例对象看上去没有任何价值。
话说这个工厂构造函数在抽象类中真的就没有价值了吗?
并不,在抽象类中的工厂构造函数可以 对该抽象类进行限制操作。
我们可以使用 factory 类名._(){ return null;} ,然后把这个类放在其他文件页面中,如果类中有且仅有工厂构造函数,那么会产生结果如下:
- 1.这个抽象类无法被extends。因为这个抽象函数中有工厂构造函数,所以不会生成默认的无参类名构造函数。同时也没自定义其他命名或类名构造函数。
- 2. 这个抽象类无法被实例化。虽然有个看似能实例化的工厂构造函数,但是他不在当前页面,同时使用_将这个工厂构造函数变成了页面私有,无法被实例。即便放在同一个页面,该工厂函数返回的也是一个null,毫无意义。
所以:抽象类经过factory一顿天秀操作之后,只能老老实实的去当一个接口了。
感谢观看,转载请注明出处。by Asery @掘金 2019年12月11日