Dart语言最佳实践

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

内容简介: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语言最佳实践》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

High Performance Python

High Performance Python

Micha Gorelick、Ian Ozsvald / O'Reilly Media / 2014-9-10 / USD 39.99

If you're an experienced Python programmer, High Performance Python will guide you through the various routes of code optimization. You'll learn how to use smarter algorithms and leverage peripheral t......一起来看看 《High Performance Python》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

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

UNIX 时间戳转换