iT邦幫忙

2022 iThome 鐵人賽

0
自我挑戰組

C++30日挑戰之旅系列 第 37

【WIDE LAB紀錄 Day7】 Dart,讓我更深層的了解你的為人

  • 分享至 

  • xImage
  •  

壹、前情提要:交接工作進度銜接

一、猜測一:是不是sdk版本本身不支援packages安裝問題

由我們主要用到的url_laucher及語言的部分可以看到若用合適的方式是可以使用packages的!
指令:flutter pub outdated,由列出的選項可以看到,若找到合適安裝方式應是可以使用套件的
https://ithelp.ithome.com.tw/upload/images/20230208/20151593bpoPd5lFHY.png
https://ithelp.ithome.com.tw/upload/images/20230208/20151593TNB8iOGNAq.png

二、猜測二:是不是macOS10.15.7不符合url_laucher版本2.5.10?

由圖所示,以Support表格來看是沒有問題的
https://ithelp.ithome.com.tw/upload/images/20230208/20151593phOmxFWbTe.png

參自: url_launcher: 6.1.9

三、自己的想法

等Dart基礎語法(到泛型為止)告一段落,就來支援研究套件安裝

貳、讓我們接續Dart語言阿囉哈:由函數(Functions)來看程式架構

一、main函數

由flutter的入口函數中我們可以看到myApp()便會作為其入口函數

void main() => runApp(MyApp());

二、可選參數

可選擇使用具名參數({param1, param2,...}),但不強迫一定要傳送

void userSettings({int age, String name}){
  //其中age與name便是可選參數,可以傳0(都不傳)~2(都傳)個參數皆可
}

三、必傳參數

@require作為前綴代表“必須傳入“的參數

void userSettings({@required int age,@required String name}){
  //其中age與name此時便為必傳參數,兩者皆必須傳送
}

四、可選的位置參數

[]標記位置為可選的位置參數

void userSettings({int age,String name,[String interests]}){
 if(interests != null){  //用if迴圈判斷可選參數的傳入值
   print('興趣為 $interests');
 }
}

五、預設參數

可以用等號賦予預設的參數值,作為Compiling時的常數

void userSettings({int age = 21,String name = 'Xian'}){
  //age預設為21,name預設為Xian
}

六、將函數當作“參數”

圖中便是將"printItem"函式作為參數使用
https://ithelp.ithome.com.tw/upload/images/20230207/20151593d40cix5jWz.png

七、將函數當作“變數”

可以用var(創立變數的方式)來創立一個函數
https://ithelp.ithome.com.tw/upload/images/20230207/20151593u5RII3hN4M.png

參、非同步設計

一、Future

官方Future class api,在非同步設計時,不會回傳尚未結束的計算結果,而是會傳其未來最終(Future)會執行完的結果
https://ithelp.ithome.com.tw/upload/images/20230207/20151593saAidfe6Fc.png

參自:Future class

二、async與await

為了解決Callback Hell的問題(如下圖所示)
https://ithelp.ithome.com.tw/upload/images/20230207/20151593ZzFpmiAojx.png

參自:Callback Hell and How to Rescue it ?

發展出一種Syntactic Sugar的方式,也就是async(需延遲的計算)await(延遲運算的佇列),以呼叫同步的過程來解決本問題
https://ithelp.ithome.com.tw/upload/images/20230207/20151593yQQDNdQ8u8.png

參自:Flutter---Google推出的跨平台框架,Android、iOS一起搞定 【Flutter基礎概念與實作】 Day5–Dart Language(3)

由下圖程式可以看到await被async所包裹(若沒有則會報錯),在Flutter網路請求的部分也會很常使用到本技巧,而往後還會延伸出isolateevent loop的概念!

//因非同步而引起的"Callback Hell"
step1('step1').then((step1Result){
  step2(step1Result).then((step2Result){
    step3(step2Result).then((step3Result){
      //step4
        //step5
          //step6...
    })
  })
})

 
//用async與await的解法
steps() async{
  String step1Result = await step1('step1');
  String step2Result = await step2(step1Result);
  String step3Result = await step3(step2Result);
  //step4
  //step5
  //step6
}

三、extends, implements,mixins

  1. extends(繼承)

Flutter屬於”單繼承“,且子類別可以由@overide的方式來重新定義父類別。範例如下

首先在main()函式外我們定義兩個class:

class Computer{ //Computer內有三個子函式,play(),work(),search()
  void play(){
    print('Computer playing');
  }
  
  void work(){
    print('Computer woking');
  }
  
  void search(){
    print('Computer searching');
  }
}

class Mac extends Computer{  //我們使用"Mac"繼承"Computer"
  void play(){
    super.play(); //調用Computer play的方式
    print('Mac playing');
  }
  
  @override //覆蓋Computer 並繼承work的方式
  void work(){
    print('Mac work');
  }
  
  void Arc(){//在Mac裡獨立新增的函式,而Arc是一個新的瀏覽器,目前只有IOS版本QQ
    print('Mac Arcing');
  }
}

而在main()函式中我們呼叫兩式方式如下

void main() {
 
  var newMac = Mac();//讓newMac變數為Mac()類別
  var normalComputer = Computer();//讓normalComputer為Computer()類別
  
  //分別呼叫各自子函式
  newMac.play();
  print('\n');//因為newMac.play()含有兩句話,用分行作為分割
  newMac.work(); 		
  newMac.search();  	
  normalComputer.search(); 	
  newMac.Arc(); 
  
}

輸出結果如下:
https://ithelp.ithome.com.tw/upload/images/20230208/20151593EXegKRKo5i.png

extends(繼承)小統整

  • 可以用extends連接父與子類別,使其繼承(如圖中的"Mac"類別變繼承了"Computer"類別)
  • 可以統整出若我們使用super.[methodName],則會同時產生父類別的函式與子類別自行定義的(像是圖中的“Computer playing“與”Mac playing”)
  • 若使用@override則代表子類別可以使用父類別中同名的method來逕自做定義而不會改到父類別的定義(像圖中的"Mac working")!
  1. implement(介面實現)

因為在dart中沒有interface(介面)等關鍵字,因此我們用A implements B的方式表達"A實作B“的概念

範例:定義class如下

class Switch{ //開關具有開的功能
  void turnOn(){
    print('Turn on the switch');
  }
}

class Light implements Switch{ //我們用"燈"來實現”開關的開功能“
  @override
  void turnOn(){
    print('Turn on the light');
  }
}

main()函式內呼叫

void main() {

  var labLight = Light();
  labLight.turnOn();
}

輸出結果
https://ithelp.ithome.com.tw/upload/images/20230208/20151593X4KsAyooHt.png

  1. mixins(混合)

在「extneds(繼承)與implements(實現)在任一個class的數量皆只能有一個!」的前提下,便有了mixins(混合)的概念,希望以A with B,也就是「A在”非繼承於B的狀況下“,可以“重複”使用B中所實作的任一field或method」

範例:定義class如下

class Plants{
  void  Photosynthesis(){//光合作用
    print('I can do photosynthesis');
  }
}

class Animal{
  void Breathe(){
    print('I can breathe');
  }
}

class Algae extends Animal with Plants{ 
  //藻類屬於傳統類別中的動物界(真菌界和原核生物界)但具植物的特徵(使用with)
  //可以看到用with後,大括號裡不需要再度定義規範
}

main()函式內呼叫如下

void main() {

  var  chlorophyta = Algae();//將綠藻定義為藻類
  chlorophyta.Photosynthesis();//直接從Plants用"with"得到的Photosynthesis
  chlorophyta.Breathe();//原本從Animal繼承的
}

輸出結果
https://ithelp.ithome.com.tw/upload/images/20230208/20151593mAU2E4upqc.png

  1. extends(繼承) v.s. implements(實現) v.s. mixins(混合)三者相互比較(相同與相異):

  • extneds(繼承)具有父子關係(也就是具有子包含於父的集合關係)
  • implements(實現)則是對單一功能或需求的”實踐或套用“,兩者不一定會有包含關係(舉例而言,動物會呼吸,使用"呼吸"這個動作可以作為植物的implements,但植物並不能extends,也就是”繼承“動物)
  • extneds(繼承)implements(實現)在任一個class的數量皆只能有一個!
  • 當我們使用with的混合方式就不用像implements需要再度定義
  • 三者的優先順序分別為extends > with > implements

肆、讓我們接續Dart語言阿囉哈:泛型

一、為什麼我們要用"泛型"?

我們希望以"泛型"達到下列兩個目的:

  1. 更有效與嚴謹的控管程式的類型檢查
  2. 減少重複程式,且在多種類型間可以定義同一個介面實現

二、記好囉,這是泛型的樣子

像是我們前幾日在看到List<E>的方式便是一種泛型的樣態。

範例,我們以List<String>限制本陣列只能存取字串的類型,若存入非字串元素則會報錯

void main() {
  List animals = new List<String>(); //用List<String>的方式指定只能存取字串類型
  animals.addAll(['小貓貓','小狗狗','小鳥鳥']);
}

報錯範例如圖,添加123(數字型態)進入animals中:
https://ithelp.ithome.com.tw/upload/images/20230208/20151593PQIQXu0ldA.png

三、如何用泛型簡化class?

我們以下列程式碼作為比較,可以看到將兩類別(當中除型別外的規範皆相同)以泛型混合為Data後便只要寫一次就可以!

小補充:<T>也可以將中間換做E,S,K等,我們以單一大寫英文字母作為型別引數

//1. 不用泛型的範例
abstract class ObjectData{ //Objsct形態的Data
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

abstract class StringData{ //String形態的Data
  String getByKey(String key);
  void setByKey(String key, String value);
}

//2.  使用泛型修改後的範例
abstract class Data<T>{
  //可以看到我們將getByKey與setByKey中的value參數的類型改為T
  T getByKey(String key);
  void setByKey(String key, T value);
}

四、泛型與extends

我們也可以透過extends來限制參數類型

class Animal {}
class Cat extends Animal {} //Cat繼承Animal
class Bird extends Animal {} //Bird繼承Animal

class NewAnimal<T extends Animal> {
  String toString() => "建立一個新的小動物:'Foo<$T>'";//Foo<$T>作為泛型接口
}

void main() {
  var cat = NewAnimal<Cat>();
  var bird = NewAnimal<Bird>();
  var animal = NewAnimal(); //沒有指定動物,維持原本extends的Animal

  print(cat);
  print(bird);
  print(animal);
}

連結之海


上一篇
【WIDE LAB紀錄 Day6】 Dart,讓我們繼續那靦腆的與你相識
下一篇
【WIDE LAB紀錄 Day8】 Flutter Packages一包一包又一包
系列文
C++30日挑戰之旅43
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言