08 | 综合案例:掌握Dart核心特性
该思维导图由 AI 生成,仅供参考
案例介绍
- 深入了解
- 翻译
- 解释
- 总结
本文以Dart语言编写购物车程序为例,逐步介绍了如何利用Dart语言特性优化程序结构和实现。作者首先展示了一个简单的购物车程序,然后通过类抽象改造和方法改造,逐步优化了程序的结构和实现。在类抽象改造中,作者利用语法糖和初始化列表简化了构造函数的赋值过程,并抽象出一个基类Meta,使程序中各个类的依赖关系更加清晰。在方法改造中,作者重载了"+”运算符,实现了商品的归纳合并,同时使用箭头函数和多行字符串声明来简化代码,使程序更加简洁和优雅。通过这些改造,读者可以深入了解Dart语言的核心特性,包括语法糖、初始化列表、重载运算符等,以及如何利用这些特性优化程序结构和实现。文章还介绍了如何使用Mixin实现类的复用,以及级联操作符“..”简化代码。总之,本文通过实例展示了Dart语言的灵活性和强大特性,对于想要深入学习Dart语言的读者来说,是一份很好的综合案例,能够帮助他们更好地掌握Dart编程的技巧和思想。
《Flutter 核心技术与实战》,新⼈⾸单¥59
全部留言(68)
- 最新
- 精选
- relax置顶能不能所有的示例代码放到一个git下面
作者回复: 代码还是要分开放的,因为每节分享都是独立的一个可运行工程,不只是代码,还有CI和单测一堆工程相关的东西,集中放就没法管理了。 我单起了一个仓库,用submodule的方式把这些demo都收进去了,可以自取 https://github.com/cyndibaby905/flutter_core_demo
2019-08-1434 - ΩItem operator+(Item item) => I... double get price => bookings... 这个改造+号的代码还是无法看懂
作者回复: operator+:把两个Item对象合并为一个。新Item对象的name为这两个对象的name拼接而成,price为他们的price相加而成。 get price:对列表数据采用累加的方式进行求和。这里用到了reduce方法。reduce是函数迭代方法,需要传递一个二元函数进行列表的合并工作。list[0...n].reduce(f)等于: a0 = list[0] a1 = f(a0,list[1]) a2 = f(a1,list[2]) an = f(an-1,list[n]) 在这里我们的f是求和函数f(x,y)=x+y,可以理解成an=list[0]+list[1]+list[n-1]+list[n]
2019-07-16335 - bytecodedouble get price() { double sum = 0.0; for(var i in bookings) { sum += i.price; } return sum; } 文中这里的price()是多了括号(),要去掉()
作者回复: 感谢提醒
2019-07-1613 - jia58960大概能看懂并实现代码,但构造函数中的属性什么时候用this.属性,什么时候不用this,希望老师能回答下。
作者回复: 类的实例变量:1.是声明时定义的,可以用this语法糖赋值;2.是继承来的,不能用this,需要把这个值交给super让父类赋值
2019-07-24210 - 灰灰abstract class PrintHelper { printInfo() => print(getInfo()); getInfo(); } class Meta { double price; String name; Meta(this.name, this.price); } // 定义商品Item类 class Item extends Meta { int count; Item(name, price, this.count): super(name, price); Item operator+(Item item) => Item(name + item.name, price * count + (item.price * item.count), 1); } //定义购物车类 class ShoppingCart extends Meta with PrintHelper { DateTime date; String code; List<Item> bookings; double get price => bookings.reduce((value, element) => (value + element)).price; // ShoppingCart(name, this.code): date = DateTime.now(), super(name,0); ShoppingCart({name}): this.withCode(name: name, code: null); ShoppingCart.withCode({name, this.code}): date = DateTime.now(), super(name, 0); getInfo () => ''' 购物车信息: ---------------- 商品名 单价 数量 总价 ---------------- ${bookings.map((item) => '${item.name} ${item.price} ${item.count} ${item.price * item.count}').join('\n ')} ================ 用户名: $name 优惠码: ${code ?? "没有"} 总价: $price 日期: $date '''; } void main() { ShoppingCart.withCode(name: '张三', code: '123456') ..bookings = [Item('苹果', 10.0, 3), Item('鸭梨', 20.0, 1)] ..printInfo(); ShoppingCart(name: '李四') ..bookings = [Item('香蕉', 2.4, 2), Item('芒果', 6.8, 4)] ..printInfo(); }
作者回复: 赞
2019-07-2725 - Uncle.Wangclass Meta { double price; String name; // 成员变量初始化语法糖 Meta(this.name, this.price); } abstract class PrintHelper { printInfo() => print(getInfo()); getInfo(); } class Item extends Meta with PrintHelper { int count; Item(name, price, {int count = 1}): super(name, price) { this.count = count; } // 重载 + 运算符,将商品对象合并为套餐商品 Item operator+(Item item) => Item(name + item.name, price + item.price); @override getInfo() => ''' 商品名: $name 单价: $price 数量: $count --------------- '''; } //with 表示以非继承的方式复用了另一个类的成员变量及函数 class ShoppingCart extends Meta with PrintHelper{ DateTime date; String code; List<Item> bookings; // 以归纳合并方式求和 double get price => bookings.reduce((value, element) => value + element).price; // 默认初始化函数,转发至 withCode 函数 ShoppingCart({name}) : this.withCode(name:name, code:null); //withCode 初始化方法,使用语法糖和初始化列表进行赋值,并调用父类初始化方法 ShoppingCart.withCode({name, this.code}) : date = DateTime.now(), super(name,0); getBookingInfo() { String str = ""; for (Item item in bookings) { str += item.getInfo(); } return str; } //?? 运算符表示为 code 不为 null,则用原值,否则使用默认值 " 没有 " @override getInfo() => ''' 购物车信息: ----------------------------- 用户名: $name 优惠码: ${code??" 没有 "} 总价: $price Date: $date 商品列表: --------------- ${ getBookingInfo() } ----------------------------- '''; } void main() { ShoppingCart.withCode(name:'张三', code:'123456') ..bookings = [Item('苹果',10.0, count: 10), Item('鸭梨',20.0, count: 5)] ..printInfo(); ShoppingCart(name:'李四') ..bookings = [Item('香蕉',15.0), Item('西瓜',40.0)] ..printInfo(); }
作者回复: 非常棒!
2019-08-203 - 江宁彭于晏class Meta { double price; String name; // 成员变量初始化语法糖 Meta(this.name, this.price); } class Item extends Meta{ Item(name, price) : super(name, price); // 重载 + 运算符,将商品对象合并为套餐商品 Item operator+(Item item) => Item(name + item.name, price + item.price); } class Items { Item item; double num; Items(this.item, this.num); double get totalPrice => item.price * num; } abstract class PrintHelper { printInfo() => print(getInfo()); getInfo(); } //with 表示以非继承的方式复用了另一个类的成员变量及函数 class ShoppingCart extends Meta with PrintHelper{ DateTime date; String code; List<Items> bookings; // 以归纳合并方式求和 double get shopPrice { double result = 0.0; bookings.forEach((element) => result+=element.totalPrice); return result; } // 默认初始化函数,转发至 withCode 函数 ShoppingCart({name}) : this.withCode(name:name, code:null); //withCode 初始化方法,使用语法糖和初始化列表进行赋值,并调用父类初始化方法 ShoppingCart.withCode({name, this.code}) : date = DateTime.now(), super(name,0); //?? 运算符表示为 code 不为 null,则用原值,否则使用默认值 " 没有 " @override String getInfo() { String bookingsResult = '商品明细: \n'; bookings.forEach((item) => bookingsResult += ' 商品名称:${item.item.name} 单价:${item.item.price} 数量 :${item.num} 总价:${item.totalPrice}\n'); return ''' 购物车信息: ----------------------------- 用户名: $name 优惠码: ${code??" 没有 "} $bookingsResult 总价: $shopPrice Date: $date ----------------------------- '''; } } void main() { ShoppingCart.withCode(name:'张三', code:'123456') ..bookings = [Items(Item('苹果',10.0),5.0), Items(Item('鸭梨',20.0),10.0)] ..printInfo(); ShoppingCart(name:'李四') ..bookings = [Items(Item('香蕉',15.0),10.0), Items(Item('西瓜',40.0),1.0)] ..printInfo(); }
作者回复: 赞👍 不过数量作为Item的属性会更好一点。
2019-07-193 - Carlo看到一段代码: firebaseService.login().then( (value) { print(value.toJson()); }, onError: (err) { print(err); }, ); 请问这个onError是怎么回事啊。为什么会被执行呢? 这是dart特有的语法么? 我所知道的一般sync function的语法是 thisFunction() .catch(console.error) .then(() => console.log('We do cleanup here'));
作者回复: 给你展开一下你就明白了: funcThatThrows() .then(successCallback, onError: (e) { handleError(e); // Original error. anotherFuncThatThrows(); // new error. }) .catchError(handleError); // Error from within then() handled.
2019-10-151 - 和小胖老师您的第二个题目,我理解是要做每一种商品的分类,但是我还是用了以前语言的思路来做了。麻烦老师,看看是否正确。 关键代码如下: //定义购物车类 class ShoppingCar extends Meta with PrintHelper { DateTime date; //购物日期 String code; //优惠券 List<Item> _bookings; //定义私有属性 num count; //商品数量 //求出总价 double get price => books.reduce((value, element) => value + element).price; List<Item> get books => _bookings; //给bookings定义set方法 set book(List<Item> bookings) { this._bookings = bookings; this.count = bookings.length; } //获取分类产品信息 String getGoodsInfo() { StringBuffer temp = new StringBuffer(); Map map = new Map<String, List<Item>>(); books.forEach((item) { if (!map.containsKey(item.name)) { List list = new List<Item>(); list.add(item); map[item.name] = list; } else { (map[item.name] as List<Item>).add(item); } }); map.forEach((k, v) { temp.write("商品名称:$k,该种商品数量:${(v as List).length},该种商品单价:${(v as List<Item>)[0].price}元\n"); print("$k,$v"); }); return temp.toString(); } //这种表达式赋值还是挺方便的 ShoppingCar.withCode({name, this.code}): this.date = DateTime.now(),super(name, 0.0); ShoppingCar({name}) : this.withCode(name: name, code: null); @override getInfo() { return ''' 购物车信息: ---------------------- 用户名:$name 优惠码:${code ?? "没有优惠券啊"} 商品总量:$count 总价:$price 日期:$date ${getGoodsInfo()} ---------------------- '''; } } 运行后信息如下:购物车信息: ---------------------- 用户名:小李 优惠码:没有优惠券啊 商品总量:3 总价:29.0 日期:2019-08-14 22:01:24.437085 商品名称:苹果,该种商品数量:2,该种商品单价:10.0元 商品名称:梨,该种商品数量:1,该种商品单价:9.0元 ----------------------
作者回复: 结果倒是对,但是这个购物车与我们理解的购物车还是有差异: 1.count属性应该是Item对象的:一个Item对象有count个,而不是一直往购物车内加同样的对象count次; 2.由于你的count属性无法拆分到Item类,导致getGoodsInfo的计算异常繁琐了。如果count属性在Item类,getGoodsInfo计算可以收敛到Item对象类内部。
2019-08-141 - 刘荣清在vs code已经安装dart、code runner插件,本地也安装完dart SDK,然后通过vs code运行dart 提示:dart : 无法将“dart”项识别为 cmdlet、函数... 但在cmd 控制板中就可以正常运行,这是为咋啊,vs code需要额外配置咋吗
作者回复: 把dart命令所在的目录设置成环境变量PATH
2019-08-031