Dart语言最佳实践

栏目: IOS · 发布时间: 5年前

内容简介:Dart支持四种集合:list、map、queue和setDart的类成员包含成员属性和方法(静态方法和实例方法)
  • 类名采用 UpperCamelCase 风格写法(即:单词首字母大写,多单词直接拼接,同 Java 中类命名规则)

  • 库和源文件使用 lowercase_with_underscores 风格(即:小写字母,多单词以 _ 分割—)

  • import的库文件设置别名时,别名也使用 lowercase_with_underscores 风格

  • 其他标识符均使用驼峰法

  • 字母缩写时,<=两个字符均大写(如:IO),>两个字符首字符大写(如:Http)

  • 不要使用字母前缀(如:Java中常见的 mName 写法)

排序

  • dart: 系列import放在最前面
  • package: 系列import放在相对import前面
  • 三方包的 package: 放在自己写的 package: 前面
  • export 写在所有 import 后面,且留白一行以分开
  • 同一优先级的import块按照字母进行排序

格式化

  • 使用 dartfmt 进行代码格式化
  • 避免一行超过80个字符的长度
  • 对所有的流式控制均使用 {}

用法

  • 引用自己的 lib 包中的文件时,考虑使用相对路径

    /// my_package
    /// └─ lib
    ///   ├─ src
    ///   │  └─ utils.dart
    ///   └─ api.dart
    
    // api.dart引用utils.dart时
    // good
    import 'src/utils.dart'; 
    
    // bad
    import 'package:my_package/src/utils.dart';
    

字符串

  • 使用相邻字符串方式进行字符串拼接

    // good
    raiseAlarm(
        'ERROR: Parts of the spaceship are on fire. Other '
        'parts are overrun by martians. Unclear which are which.');
    
    // bad
    raiseAlarm('ERROR: Parts of the spaceship are on fire. Other ' +
        'parts are overrun by martians. Unclear which are which.');
    
  • 使用插值方式组合字符串和值

    // good
    'Hello, $name! You are ${year - birth} years old.';
    
    // bad
    'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';
    
  • 插值时避免使用 {} ,除非必要

    // good
    'Hi, $name!'
    "Wear your wildest $decade's outfit."
    'Wear your wildest ${decade}s outfit.'
        
    // bad
    'Hi, ${name}!'
    "Wear your wildest ${decade}'s outfit."
    

集合

Dart支持四种集合:list、map、queue和set

  • 使用集合简化式进行实例化

    // good
    var points = [];
    var addresses = {};
    
    // bad
    var points = List();
    var addresses = Map();
    
  • 不要使用 .length 来判断集合是否为空

    // good
    if (lunchBox.isEmpty) return 'so hungry...';
    if (words.isNotEmpty) return words.join(' ');
    
    // bad
    if (lunchBox.length == 0) return 'so hungry...';
    if (!words.isEmpty) return words.join(' ');
    
  • 推荐使用集合的高阶函数进行相关处理(如: wheremap

    // good
    var aquaticNames = animals
        .where((animal) => animal.isAquatic)
        .map((animal) => animal.name);
    
  • 避免在 Iterable.forEach() 中处理方法体编码

    // good
    for (var person in people) {
      ...
    }
    
    // bad
    people.forEach((person) {
      ...
    });
    
    // 只有forEach中是函数指针, 才鼓励使用, 如下
    people.forEach(print);
    
  • 除了要改变结果类型,否则不要使用 List.from() 方法

    // good
    // Creates a List<int>:
    var iterable = [1, 2, 3];
    
    // Prints "List<int>":
    print(iterable.toList().runtimeType);
    
    // bad
    // Creates a List<int>:
    var iterable = [1, 2, 3];
    
    // Prints "List<dynamic>":
    print(List.from(iterable).runtimeType);
    

    需要改变类型时,可使用该方法

    // good
    var numbers = [1, 2.3, 4]; // List<num>.
    numbers.removeAt(1); // Now it only contains integers.
    var ints = List<int>.from(numbers);
    
  • 使用 whereType() 来进行集合的类型过滤

    // good
    var objects = [1, "a", 2, "b", 3];
    var ints = objects.whereType<int>();
    
    // bad
    var objects = [1, "a", 2, "b", 3];
    var ints = objects.where((e) => e is int);
    
    // bad
    var objects = [1, "a", 2, "b", 3];
    var ints = objects.where((e) => e is int).cast<int>();
    
  • 当有更方便的操作符可使用时,不要使用 cast

    // good
    var stuff = <dynamic>[1, 2];
    var ints = List<int>.from(stuff);
    
    // bad
    var stuff = <dynamic>[1, 2];
    var ints = stuff.toList().cast<int>();
    
    
    // good
    var stuff = <dynamic>[1, 2];
    var reciprocals = stuff.map<double>((n) => 1 / n);
    
    // bad
    var stuff = <dynamic>[1, 2];
    var reciprocals = stuff.map((n) => 1 / n).cast<double>();
    
  • 尽量避免使用 cast 关键字

    • 当有更方便的操作符可使用时,不要使用 cast

      // good
      var stuff = <dynamic>[1, 2];
      var ints = List<int>.from(stuff);
      
      // bad
      var stuff = <dynamic>[1, 2];
      var ints = stuff.toList().cast<int>();
      
      
      // good
      var stuff = <dynamic>[1, 2];
      var reciprocals = stuff.map<double>((n) => 1 / n);
      
      // bad
      var stuff = <dynamic>[1, 2];
      var reciprocals = stuff.map((n) => 1 / n).cast<double>();
      
    • 创建集合时,通过指定泛型类型代替 cast

      // good
      List<int> singletonList(int value) {
        var list = <int>[];
        list.add(value);
        return list;
      }
      
      // bad
      List<int> singletonList(int value) {
        var list = []; // List<dynamic>.
        list.add(value);
        return list.cast<int>();
      }
      
    • 遍历集合每项元素时,使用 as 代替 cast

      // good
      void printEvens(List<Object> objects) {
        // We happen to know the list only contains ints.
        for (var n in objects) {
          if ((n as int).isEven) print(n);
        }
      }
      
      // bad
      void printEvens(List<Object> objects) {
        // We happen to know the list only contains ints.
        for (var n in objects.cast<int>()) {
          if (n.isEven) print(n);
        }
      }
      
    • 需要对集合中的大多数元素做操作时,使用 List.from() 代替 cast

      // good
      int median(List<Object> objects) {
        // We happen to know the list only contains ints.
        var ints = List<int>.from(objects);
        ints.sort();
        return ints[ints.length ~/ 2];
      }
      
      // bad
      int median(List<Object> objects) {
        // We happen to know the list only contains ints.
        var ints = objects.cast<int>();
        ints.sort();
        return ints[ints.length ~/ 2];
      }
      

    注:

    cast()
    cast()
    

函数

  • 使用函数声明式来绑定函数和名称

    // good
    void main() {
      localFunction() {
        ...
      }
    }
    
    // bad
    void main() {
      var localFunction = () {
        ...
      };
    }
    
  • 能够传递函数指针时不要取传递冗余的lambda式

    // good
    names.forEach(print);
    
    // bad
    names.forEach((name) {
      print(name);
    });
    

参数

  • 命名可选参数 中指定默认参数时使用 = 代替 :

    // good
    void insert(Object item, {int at = 0}) { ... }
    
    // bad
    void insert(Object item, {int at: 0}) { ... }
    

    注: : 作为指定默认参数值的用法,正在被废弃

  • 不要显式地置顶参数默认值为 null

    // good
    void error([String message]) {
      stderr.write(message ?? '\n');
    }
    
    // bad
    void error([String message = null]) {
      stderr.write(message ?? '\n');
    }
    

    注: 不指定默认参数值,Dart语言机制默认指定为 null ,不需要冗余指定

变量

  • 初始化变量时不要显式地置顶变量为 null

    // good
    int _nextId;
    
    class LazyId {
      int _id;
    
      int get id {
        if (_nextId == null) _nextId = 0;
        if (_id == null) _id = _nextId++;
    
        return _id;
      }
    }
    
    
    // bad
    int _nextId = null;
    
    class LazyId {
      int _id = null;
    
      int get id {
        if (_nextId == null) _nextId = 0;
        if (_id == null) _id = _nextId++;
    
        return _id;
      }
    }
    
  • 不要存储你能计算的值

    // bad
    class Circle {
      num radius;
      num area;
      num circumference;
    
      Circle(num radius)
          : radius = radius,
            area = pi * radius * radius,
            circumference = pi * 2.0 * radius;
    }
    
    
    // bad
    class Circle {
      num _radius;
      num get radius => _radius;
      set radius(num value) {
        _radius = value;
        _recalculate();
      }
    
      num _area;
      num get area => _area;
    
      num _circumference;
      num get circumference => _circumference;
    
      Circle(this._radius) {
        _recalculate();
      }
    
      void _recalculate() {
        _area = pi * _radius * _radius;
        _circumference = pi * 2.0 * _radius;
      }
    }
    
    
    // good
    class Circle {
      num radius;
    
      Circle(this.radius);
    
      num get area => pi * radius * radius;
      num get circumference => pi * 2.0 * radius;
    }
    

    主要出于以下方面考虑:

    radius
    

    注:这里针对的是普通计算,而对于开销较大的计算,仍然要考虑存储结果作为cache。

类成员

Dart的类成员包含成员属性和方法(静态方法和实例方法)

  • 不要给成员属性设置 settergetter 方法,除非必须

    // good
    class Box {
      var contents;
    }
    
    // bad
    class Box {
      var _contents;
      get contents => _contents;
      set contents(value) {
        _contents = value;
      }
    }
    
  • 对于只读的成员属性,尽量用 final 修饰

    // good
    class Box {
      final contents = [];
    }
    
    // bad
    class Box {
      var _contents;
      get contents => _contents;
    }
    

    注:对于需要在构造方法之外复制的成员属性,可使用private的成员属性 + public的getter方法来满足

  • 对于简单的成员变量/方法可考虑使用 =>

    // good
    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)}';
    

    但不要为了使用 => 而强行将多行表达式进行聚合,如:

    // good
    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;
    }
    
    // bad
    Treasure openChest(Chest chest, Point where) =>
        _opened.containsKey(chest) ? null : _opened[chest] = Treasure(where)
          ..addAll(chest.contents);
    

    还可以对无返回值的成员使用 =>

    // good
    num get x => center.x;
    set x(num value) => center = Point(value, center.y);
    
  • 除非你必须要使用 this 避免歧义,否则不要使用它

    // good
    class Box {
      var value;
    
      void clear() {
        update(null);
      }
    
      void update(value) {
        this.value = value;
      }
    }
    
    // bad
    class Box {
      var value;
    
      void clear() {
        this.update(null);
      }
    
      void update(value) {
        this.value = value;
      }
    }
    

    另,Dart语言中,构造方法里不会出现成员变量与构造方法参数同名的歧义问题,如下写法是合法的

    // good
    class Box extends BaseBox {
      var value;
    
      Box(value)
          : value = value,
            super(value);
    }
    
  • 尽可能在成员变量声明时进行初始化

    // good
    class Folder {
      final String name;
      final List<Document> contents = [];
    
      Folder(this.name);
      Folder.temp() : name = 'temporary';
    }
    
    // bad
    class Folder {
      final String name;
      final List<Document> contents;
    
      Folder(this.name) : contents = [];
      Folder.temp() : name = 'temporary'; // Oops! Forgot contents.
    }
    

构造方法

  • 尽可能使用标准构造式

    注:标准构造式,官方原文是 initializing formals ,参代码

    // good
    class Point {
      num x, y;
      Point(this.x, this.y);
    }
    
    // bad
    class Point {
      num x, y;
      Point(num x, num y) {
        this.x = x;
        this.y = y;
      }
    }
    
  • 不要在标准构造式中指定类型

    // good
    class Point {
      int x, y;
      Point(this.x, this.y);
    }
    
    // bad
    class Point {
      int x, y;
      Point(int this.x, int this.y);
    }
    
  • 对于空方法体的构造方法,使用 ; 来替换 {}

    // good
    class Point {
      int x, y;
      Point(this.x, this.y);
    }
    
    // bad
    class Point {
      int x, y;
      Point(this.x, this.y) {}
    }
    
  • 不要使用关键字 new

    Dart1中的关键字 new 是需要的,单Dart2已经将其变为可选的了。官方为了减轻升级迁移的负担,仍然支持了 new 关键字,但不建议使用。

    // good
    Widget build(BuildContext context) {
      return Row(
        children: [
          RaisedButton(
            child: Text('Increment'),
          ),
          Text('Click!'),
        ],
      );
    }
    
    // bad
    Widget build(BuildContext context) {
      return new Row(
        children: [
          new RaisedButton(
            child: new Text('Increment'),
          ),
          new Text('Click!'),
        ],
      );
    }
    
  • 不要使用冗余的关键字 const

    // good
    const primaryColors = [
      Color("red", [255, 0, 0]),
      Color("green", [0, 255, 0]),
      Color("blue", [0, 0, 255]),
    ];
    
    // bad
    const primaryColors = const [
      const Color("red", const [255, 0, 0]),
      const Color("green", const [0, 255, 0]),
      const Color("blue", const [0, 0, 255]),
    ];
    

异步操作

  • 尽可能使用 asyncawait 来替换 Future 的链式风格

    // good
    Future<int> countActivePlayers(String teamName) async {
      try {
        var team = await downloadTeam(teamName);
        if (team == null) return 0;
    
        var players = await team.roster;
        return players.where((player) => player.isActive).length;
      } catch (e) {
        log.error(e);
        return 0;
      }
    }
    
    
    // bad
    Future<int> countActivePlayers(String teamName) {
      return downloadTeam(teamName).then((team) {
        if (team == null) return Future.value(0);
    
        return team.roster.then((players) {
          return players.where((player) => player.isActive).length;
        });
      }).catchError((e) {
        log.error(e);
        return 0;
      });
    }
    
  • 在关键字 async 并无实际效果时不要使用它

    // good
    Future afterTwoThings(Future first, Future second) {
      return Future.wait([first, second]);
    }
    
    // bad
    Future afterTwoThings(Future first, Future second) async {
      return Future.wait([first, second]);
    }
    

    以下几种场景, async 是有实际效果的

    • 使用了 await 关键字
    • 返回一个异步错误时, async + throwreturn Future.error(...) 的简写形式
    • 你想在你方法的返回值中隐式包一个 Future

    以上三种情况对应代码如下

    // good
    Future usesAwait(Future later) async {
      print(await later);
    }
    
    Future asyncError() async {
      throw 'Error!';
    }
    
    Future asyncValue() async => 'value';
    
  • 考虑使用高阶函数来处理流

    Streams提供了一系列高阶函数对流数据进行操作处理,建议优先考虑使用高阶函数

  • 避免直接使用 Completer 对象

    // bad
    Future<bool> fileContainsBear(String path) {
      var completer = Completer<bool>();
    
      File(path).readAsString().then((contents) {
        completer.complete(contents.contains('bear'));
      });
    
      return completer.future;
    }
    
    
    // good
    Future<bool> fileContainsBear(String path) {
      return File(path).readAsString().then((contents) {
        return contents.contains('bear');
      });
    }
    
    
    // good
    Future<bool> fileContainsBear(String path) async {
      var contents = await File(path).readAsString();
      return contents.contains('bear');
    }
    
  • FutureOr<T> 对象进行消除歧义时,优先判断 Future<T>

    // good
    Future<T> logValue<T>(FutureOr<T> value) async {
      if (value is Future<T>) {
        var result = await value;
        print(result);
        return result;
      } else {
        print(value);
        return value as T;
      }
    }
    
    // bad
    Future<T> logValue<T>(FutureOr<T> value) async {
      if (value is T) {
        // 传递Future<Object>时会永远走该分支
        print(value);
        return value;
      } else {
        var result = await value;
        print(result);
        return result;
      }
    }
    

以上所述就是小编给大家介绍的《Dart语言最佳实践》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

当下的启蒙

当下的启蒙

[美] 史迪芬·平克 / 侯新智、欧阳明亮、魏薇 / 浙江人民出版社 / 2018-12 / 159.90

[编辑推荐] ● 比尔•盖茨最喜爱的一本书。理查德·道金斯心中的诺贝尔文学奖作品。尤瓦尔•赫拉利2018年最爱的书之一。 ● 当代最伟大思想家史蒂芬·平克全面超越自我的巅峰之作,一部关于人类进步的英雄史诗。 ●《当下的启蒙》用数据和事实揭示出世界的真相:不是黑暗,而是光明;不是丧,而是燃;我们没有退步,而是一直在进步,还将继续进步。用这本书点燃生活的勇气,亲手创造更美好的未来。 ......一起来看看 《当下的启蒙》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换