來學習了解下Flutter及Dart 編碼規范?


dart官方文檔中的 【Effective Dart】

規范主要分為四個部分:

  • 樣式規范
  • 文檔規范
  • 使用規范
  • 設計規范

每個部分都有許多的例子說明,每個例子都會以下面五個詞中的某些作為開頭:

  • DO :表示你需要遵守的做法
  • DONT :表示這樣的做法是非常不好的
  • PREFER :在多數情況下,都推薦的做法
  • AVOID: 在多數情況下,都應該避免的做法
  • CONSIDER: 需要你自己去斟酌的做法

在我看來,編碼習慣都是因人而異的,並沒有所謂的最佳方案。

如果你是一個人開發,當然不需要在意這些問題,但是如果你的代碼需要展現給別人,或者你需要與別人協同開發,編碼規范就非常有必要了。

下面,將會從官方文檔中選取最基本,最典型,發生率較高的一些情況,作為規范說明。

✅表示正面做法,❌表示反面做法

樣式規范

命名

DO: 類, 枚舉, 類型定義, 以及泛型,都需要使用大寫開頭的駝峰命名法
class SliderMenu { ... } class HttpRequest { ... } typedef Predicate<T> = bool Function(T value); 

在使用注解時候,也應該這樣

class Foo { const Foo([arg]); } @Foo(anArg) class A { ... } @Foo() class B { ... } 

不過為一個類的構造函數添加注解時,你可能需要創建一個小寫開頭的注解變量

const foo = Foo(); @foo class C { ... } 

DO: 命名庫、包、目錄、dart文件都應該是小寫加上下划線

✅

library peg_parser.source_scanner;

import 'file_system.dart'; import 'slider_menu.dart'; 
❌

library pegparser.SourceScanner;

import 'file-system.dart'; import 'SliderMenu.dart'; 

DO: 將引用使用as轉換的名字也應該是小寫下划線

import 'dart:math' as math; import 'package:angular_components/angular_components' as angular_components; import 'package:js/js.dart' as js; 
import 'dart:math' as Math; import 'package:angular_components/angular_components' as angularComponents; import 'package:js/js.dart' as JS; 

DO: 變量名、方法、參數名都應該是小寫開頭的駝峰命名法

✅

var item;

HttpRequest httpRequest;

void align(bool clearItems) { // ... } 
const pi = 3.14; const defaultTimeout = 1000; final urlScheme = RegExp('^([a-z]+):'); class Dice { static final numberGenerator = Random(); } 
const PI = 3.14; const DefaultTimeout = 1000; final URL_SCHEME = RegExp('^([a-z]+):'); class Dice { static final NUMBER_GENERATOR = Random(); } 

花括號

DO: 只有一個if語句且沒有else的時候,並且在一行內能夠很好的展示,就可以不用花括號

if (arg == null) return defaultValue; 

但是如果一行內展示比較勉強的話,就需要用花括號了:

if (overflowChars != other.overflowChars) { return overflowChars < other.overflowChars; } 
if (overflowChars != other.overflowChars) return overflowChars < other.overflowChars; 

文檔規范

DO: 在dart的注釋中,更加推薦使用///而非//

✅

/// The number of characters in this chunk when unsplit. int get length => ... 
// The number of characters in this chunk when unsplit. int get length => ... 

至於為什么要這樣做,官方表示是由於歷史原因以及他們覺得這個在某些情況下看起來更方便閱讀。

DO: 文檔注釋應該以一句簡明的話開頭

/// Deletes the file at [path] from the file system. void delete(String path) { ... } 
❌

/// Depending on the state of the file system and the user's permissions, /// certain operations may or may not be possible. If there is no file at /// [path] or it can't be accessed, this function throws either [IOError] /// or [PermissionError], respectively. Otherwise, this deletes the file. void delete(String path) { ... } 

DO: 將注釋的第一句與其他內容分隔開來

/// Deletes the file at [path]. /// /// Throws an [IOError] if the file could not be found. Throws a /// [PermissionError] if the file is present but could not be deleted. void delete(String path) { ... } 
/// Deletes the file at [path]. Throws an [IOError] if the file could not /// be found. Throws a [PermissionError] if the file is present but could /// not be deleted. void delete(String path) { ... } 

DO: 使用方括號去聲明參數、返回值以及拋出的異常

/// Defines a flag with the given name and abbreviation. /// /// @param name The name of the flag. /// @param abbr The abbreviation for the flag. /// @returns The new flag. /// @throws ArgumentError If there is already an option with /// the given name or abbreviation. Flag addFlag(String name, String abbr) => ... 
/// Defines a flag. /// /// Throws an [ArgumentError] if there is already an option named [name] or /// there is already an option using abbreviation [abbr]. Returns the new flag. Flag addFlag(String name, String abbr) => ... 

使用規范

依賴

PREFER: 推薦使用相對路徑導入依賴

如果項目結構如下:

my_package └─ lib ├─ src │ └─ utils.dart └─ api.dart 

想要在 api.dart 中導入 utils.dart

import 'src/utils.dart'; 
import 'package:my_package/src/utils.dart'; 

賦值

** DO: 使用??將null值做一個轉換**

在dart中 ?? 操作符表示當一個值為空時會給它賦值 ?? 后面的數據

if (optionalThing?.isEnabled) { print("Have enabled thing."); } 

當 optionalThing 為空的時候,上面就會有空指針異常了。

這里說明一下。 ?. 操作符相當於做了一次判空操作,只有當 optionalThing 不為空的時候才會調用 isEnabled 參數,當 optionalThing 為空的話默認返回null,用在if判斷句中自然就不行了

下面是正確做法

// 如果為空的時候你想返回false的話: optionalThing?.isEnabled ?? false; // 如果為空的時候你想返回ture的話: optionalThing?.isEnabled ?? true; 
❌

optionalThing?.isEnabled == true; optionalThing?.isEnabled == false; 

字符串

在dart中,不推薦使用 + 去連接兩個字符串

DO: 使用回車鍵直接分隔字符串

✅

raiseAlarm(
    'ERROR: Parts of the spaceship are on fire. Other ' 'parts are overrun by martians. Unclear which are which.') 
❌

raiseAlarm('ERROR: Parts of the spaceship are on fire. Other ' + 'parts are overrun by martians. Unclear which are which.'); 

PREFER: 使用${}來連接字符串與變量值

'Hello, $name! You are ${year - birth} years old.'; 
'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...'; 

集合

dart中創建空的可擴展 List 有兩種方法: [] 和 List();創建空的 HashMap 有三種方法: {}, Map(),和 LinkedHashMap()

如果要創建不可擴展的列表或其他一些自定義集合類型,那么務必使用構造函數。

DO: 盡可能使用簡單的字面量創建集合

var points = []; var addresses = {}; 
var points = List(); var addresses = Map(); 

當你想要指定類型的時候

var points = <Point>[]; var addresses = <String, Address>{}; 
var points = List<Point>(); var addresses = Map<String, Address>(); 

DON’T: 不要使用.lenght的方法去表示一個集合是空的

if (lunchBox.isEmpty) return 'so hungry...'; if (words.isNotEmpty) return words.join(' '); 
if (lunchBox.length == 0) return 'so hungry...'; if (!words.isEmpty) return words.join(' '); 

CONSIDER: 考慮使用高階方法轉換序列

var aquaticNames = animals .where((animal) => animal.isAquatic) .map((animal) => animal.name); 

AVOID: 避免使用帶有函數字面量的Iterable.forEach()

forEach()函數在JavaScript中被廣泛使用,因為內置的for-in循環不能達到你通常想要的效果。在Dart中,如果要迭代序列,那么慣用的方法就是使用循環。

for (var person in people) { ... } 
❌

people.forEach((person) { ... }); 

DON’T: 不要使用 List.from() 除非你打算更改結果的類型

有兩種方法去獲取 Iterable,分別是List.from()和Iterable.toList()

// 創建一個List<int>: var iterable = [1, 2, 3]; // 輸出"List<int>": print(iterable.toList().runtimeType); 
// 創建一個List<int>: var iterable = [1, 2, 3]; // 輸出"List<dynamic>": print(List.from(iterable).runtimeType); 

DO: 使用 whereType()去用類型過濾一個集合

var objects = [1, "a", 2, "b", 3]; var ints = objects.where((e) => e is int); 
var objects = [1, "a", 2, "b", 3]; var ints = objects.where((e) => e is int).cast<int>(); 
var objects = [1, "a", 2, "b", 3]; var ints = objects.whereType<int>(); 

參數

DO: 使用 = 給參數設置默認值
void insert(Object item, {int at = 0}) { ... } 
void insert(Object item, {int at: 0}) { ... } 

DON’T: 不要將參數的默認值設置為 null

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

變量

AVOID: 避免存儲可以計算的值

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; } } 
class Circle { num radius; Circle(this.radius); num get area => pi * radius * radius; num get circumference => pi * 2.0 * radius; } 

成員

DON’T: 不要寫沒必要的getter 和 setter

class Box { var contents; } 
class Box { var _contents; get contents => _contents; set contents(value) { _contents = value; } } 

構造函數

DO: 盡可能使用簡單的初始化形式

class Point { num x, y; Point(num x, num y) { this.x = x; this.y = y; } } 
class Point { num x, y; Point(this.x, this.y); } 

DON’T: 不要使用 new 來創建對象

dart中不需要new

Widget build(BuildContext context) { return Row( children: [ RaisedButton( child: Text('Increment'), ), Text('Click!'), ], ); } 
Widget build(BuildContext context) { return new Row( children: [ new RaisedButton( child: new Text('Increment'), ), new Text('Click!'), ], ); } 

DON’T: 不要使用多余的 const 修飾對象

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

異常處理

: 使用 rethrow 重新拋出異常

try { somethingRisky(); } catch (e) { if (!canHandle(e)) throw e; handle(e); } 
try { somethingRisky(); } catch (e) { if (!canHandle(e)) rethrow; handle(e); } 

設計

AVOID: 避免為了實現流式調用而讓方法返回this

var buffer = StringBuffer() ..write('one') ..write('two') ..write('three'); 
var buffer = StringBuffer() .write('one') .write('two') .write('three'); 

AVOID: 避免使用 FutureOr<T> 作為返回類型

Future<int> triple(FutureOr<int> value) async => (await value) * 3; 
FutureOr<int> triple(FutureOr<int> value) { if (value is int) return value * 3; return (value as Future<int>).then((v) => v * 3); } 

AVOID: 避免將bool值直接作為輸入參數

new Task(true); new Task(false); new ListBox(false, true, true); new Button(false); 
Task.oneShot(); Task.repeating(); ListBox(scroll: true, showScrollbars: true); Button(ButtonState.enabled); 

DON’T: 不要在自定義的 == operator 方法中進行判空

class Person { final String name; // ··· bool operator ==(other) => other is Person && name == other.name; int get hashCode => name.hashCode; } 
class Person { final String name; // ··· bool operator ==(other) => other != null && ... } 

最后

如果你覺得文章寫得不錯就給個贊唄?如果你覺得那里值得改進的,請給我留言。一定會認真查詢,修正不足。謝謝。

希望讀到這的您能轉發分享和關注一下我,以后還會更新技術干貨,謝謝您的支持!

這里,筆者分享一份從架構哲學的層面來剖析的視頻及資料分享給大家梳理了多年的架構經驗,籌備近1個月最新錄制的,相信這份視頻能給你帶來不一樣的啟發、收獲


 
image.png
image.png

 

領取方式:獲取免費架構視頻資料請查看我主頁

轉發+點贊+關注,第一時間獲取最新知識點

Android架構師之路很漫長,一起共勉吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM