開發與維運

【Dart 專題】Factory 工廠構造函數

      小菜學習 Flutter 有一段時間,雖可以應用基本的 Dart 語法,但對於一些特殊的語法還是很陌生,小菜準備開一個小的【Dart 專題】記錄一些日常用的 Dart 語法及相關應用;

Constructors

      Constructors 構造方法在日常應用中必不可少,小菜是 Android 開發,對 Java 構造函數更加熟悉;

      Constructors 構造方法是對象的初始化;函數名與類名一致且沒有返回值類型;默認是無參構造函數,可以通過重載方式設置多個函數名相同的構造函數;

      而 Dart 構造函數與 Java 略有不同,小菜簡單嘗試;

構造函數類型

      Dart 構造函數主要分為四類,分別是 Default Constructors 默認構造函數、Named Constructors 命名構造函數、Constant Constructors 常量構造函數和 Factory Constructors 工廠構造函數;

Default Constructors

      默認構造函數與 Java 類似,可以是無參構造函數和有參構造函數;但與 Java 不同的是,Dart 構造函數不允許重載,即不允許有相同名稱的構造函數;

無參構造函數

      如果不聲明構造函數,則會提供默認無參構造函數;

class People {
  People() {
    print('Dart --> People()');
  }
}
有參構造函數

      Dart 還提供了簡易的語法糖的方式優化代碼格式;

class People {
  String name;
  int age, sex;

  /// 不可與無參構造函數同時出現
  People(name, age, {sex}) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    print('Dart --> People($name, $age, {$sex})');
  }
  
  /// 簡易語法糖
  People(this.name, this.age, {this.sex});
}

      當子類繼承父類時,初始化子類構造函數會優先初始化父類構造函數;繼承時需要使用 super() 父類構造函數,若父類為無參構造函數時可以省略;

class Student extends People {
  Student(name, age, {sex}) : super() {
    this.name = name;
    this.age = age;
    this.sex = sex;
    print('Dart --> Student($name, $age, {$sex}) extends People()');
  }
}
People people = People();
print('<---------------->');
Student student = Student('阿策小和尚', 100, sex: 0);

Named Constructors

      使用命名構造函數可以為實現多個構造函數或提供更清晰的構造函數;同時子類需要實現 super() 構造函數類型完全取決於父類構造函數類型;其中命名構造函數是不允許被繼承的,若子類需要實現與父類同名的命名構造函數,則需要調用父類的同名的命名構造函數;

People.fromMap(map) {
  this.name = map['name'];
  this.age = map['age'];
  this.sex = map['sex'];
  print('Dart --> People.fromMap($map) --> $name, $age, $sex');
}

Student.fromMap(map) : super.fromMap(map) {
  this.name = map['name'];
  this.age = map['age'];
  this.sex = map['sex'];
  print('Dart --> Student.fromMap($map) extends People() --> $name,$age,$sex');
}

Map map = {'name': '阿策小和尚', 'age': 100, 'sex': 0};
People people = People.fromMap(map);
print('<---------------->');
Student student = Student.fromMap(map);

Constant Constructors

      如果生成類的對象是不會變的,可以定義常量構造函數;

  1. 其中所有實例變量都是 final 類型的,類中不允許有普通變量類型,因此其變量在構造函數完成之後不允許變更;
  2. 變量中不允許有初始值;
  3. 常量構造函數必須用 const 關鍵詞修飾;
  4. 常量構造函數不允許有函數體;
  5. 實例化時需要加 const 否則實例化的對象仍然可以修改變量值;
class People {
  final String name ;
  final int age ;
  final int sex;
  const People(this.name, this.age, {this.sex});
}

class Student extends People {
  Student(name, age, {sex}) : super(name, age, sex: sex){
    print('Dart --> Student($name, $age, {$sex}) extends People() --> $name, $age, $sex');
  }
}

const People people = People('阿策小和尚', 100, sex: 0);
print('People.name=${people.name}, age=${people.age}, sex=${people.sex}');
print('<---------------->');
Student student = Student('阿策小和尚', 100, sex: 0);
print('Student.name=${student.name}, age=${student.age}, sex=${student.sex}');

Factory Constructors

      工廠構造函數不需要每次構建新的實例,且不會自動生成實例,而是通過代碼來決定返回的實例對象;工廠構造函數類似於 static 靜態成員,無法訪問 this 指針;一般需要依賴其他類型構造函數;工廠構造函數還可以實現單例;

class People {
  String name;
  int age, sex;
  static People _cache;

  People() {
    print('Dart --> People()');
  }

  People.fromMap(map) {
    this.name = map['name'];
    this.age = map['age'];
    this.sex = map['sex'];
    print('Dart --> People.fromMap($map) --> $name, $age, $sex');
  }

  factory People.map(map) {
    if (People._cache == null) {
      People._cache = new People.fromMap(map);
      print('Dart --> People.map($map) --> ${map['name']}, ${map['age']}, ${map['sex']} --> People._cache == null');
    }
    print('Dart --> People.map($map) --> ${map['name']}, ${map['age']}, ${map['sex']}');
    return People._cache;
  }

  factory People.fromJson(json) => People();
}

Map map = {'name': '阿策小和尚', 'age': 100, 'sex': 0};
People people = People.map(map);
print('People.name=${people.name}, age=${people.age}, sex=${people.sex}, hashCode=${people.hashCode}');
print('<---------------->');
People people2 = People.map(map);
print('People2.name=${people2.name}, age=${people2.age}, sex=${people2.sex}, hashCode=${people2.hashCode}');
print('<---------------->');
People people3 = People.fromMap(map);
print('People3.name=${people3.name}, age=${people3.age}, sex=${people3.sex}, hashCode=${people3.hashCode}');
print('<---------------->');
People people4 = People.fromJson(map);
print('People4.name=${people4.name}, age=${people4.age}, sex=${people4.sex}, hashCode=${people4.hashCode}');

      小菜先定義一個 _cache 緩存,在使用工廠構造函數 People.map() 時,先判斷該實例是否已完成構造,若已存在則返回 _cache 實例,不存在則構建新的實例;如 Demo 中的 peoplepeople2,調用工廠函數時,people 是第一次構建,people2 在構建時 _cache 中已存在,因此無需重新構建;其中 peoplepeople2 對應的 HashCode 一致,說明兩者是相同的對象;

注意事項

1. 構造函數具有傳遞性

      若在聲明構造函數時,多個函數之間有類似的邏輯關聯,為了減少代碼冗餘,可以通過函數傳遞來精簡代碼;小菜創建了一個 People.fromAdd() 構造函數,對於相同地方的 People 可以通過 People.fromBJ() 來傳遞到 People.fromAdd() 來實現;

People.fromAdd(this.name, this.address);
People.fromBJ(name) : this.fromAdd(name, '北京');

People people6 = People.fromAdd('阿策小和尚', '北京');
print('People6.name=${people6.name}, address=${people6.address}, hashCode=${people6.hashCode}');
People people7 = People.fromBJ('阿策小和尚');
print('People7.name=${people7.name}, address=${people7.address}, hashCode=${people7.hashCode}');

2. Factory 工廠構造函數可以實現單例

class Singleton {
  static final Singleton _singleton = Singleton.internal();

  factory Singleton() => _singleton;

  Singleton.internal();
}

      小菜對很多特殊情況的研究還不夠全面,如有錯誤請多多指導!

來源: 阿策小和尚

Leave a Reply

Your email address will not be published. Required fields are marked *