Flutter 入門與實戰(二十八):Dio 封裝之金屋藏嬌

島上碼農發表於2021-07-14

本文已參與好文召集令活動,點選檢視:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!

楔子

有這麼一個純屬虛構的故事,說小王在成為老王之前,為人處事很謹慎。譬如說,他有了女朋友,他呢又想讓別人知道自己有女朋友了,但是又不想讓別人知道自己的女朋友是誰。很矛盾是吧?這就是小王的謹慎之處,萬一哪天換女朋友了(說不定換得還挺勤快),不至於被人嘲笑亂來,或者被人稱為“渣男”。

image.png

怎麼辦呢?小王到底是學過歷史的人,想起了漢武帝劉徹年少時候的“金屋藏嬌”的故事,於是就重演了這個歷史。

小王的女友

小王的女友年輕漂亮,起了個英文名字叫“Dio”,據說是 pub 上的交際花,大家基本上相互聯絡都靠她。小王很低調,不想讓別人知道她的女友是 Dio,於是對外只說自己有女朋友,並且對外宣稱這個女朋友叫做 HttpUtil。目前HttpUtil其實就是披了外衣的 Dio,但是哪天小王換成別的女友了,別人也看不出來——因為從外面看,都是叫 HttpUtil。小王暗地裡稱之為:Dio 之金屋藏嬌。

image.png

小王利用HttpUtil向外面展示了 Dio 的交際能力,譬如GetPutPostPatchDeleteUpload。這樣小王很有面子,因為大家都覺得小王的女朋友很厲害——雖然沒有見過面。很多年以後,小王變成老王后,老王的兒子僱傭了一批程式設計師,才知道程式設計師早就發明了這種技巧,還取了個專門的術語,叫做封裝

HttpUtil

最開始的時候,小王的HttpUtil長這樣。但是發現介紹女朋友的每一個交際能力都得吧啦吧啦講一堆如何處理交際過程的尷尬場面(異常),也太麻煩了!

class HttpUtil {
  static Dio _dioInstance;
  static Dio getDioInstance() {
    if (_dioInstance == null) {
      _dioInstance = Dio();
    }

    return _dioInstance;
  }

  static Future get(String url, {Map<String, dynamic> queryParams}) async {
    try{
      return await HttpUtil.getDioInstance().get(url, queryParameters: queryParams);
    } on DioError catch (e) {
      EasyLoading.showError(e.message);
    } on Exception catch (e) {
      EasyLoading.showError(e.toString());
    }
  }
  
  //...
}
複製程式碼

小王省事,做了一次改進,把所有的能力都通過另一個方式一起講,這樣講的時候把全部技能都一遍的時候,只需要介紹一次如何處理尷尬場面就好了,畢竟尷尬場面的處理都是差不多的。首先,小王如數家珍似的定義了女朋友的能力。

enum HttpMethod {
  GET,
  PUT,
  POST,
  PATCH,
  DELETE,
  UPLOAD,
}
複製程式碼

接著,定義了他介紹女朋友的話術:

static Future sendRequest(HttpMethod method, String url,
      {Map<String, dynamic> queryParams, dynamic data}) async {
  try {
    switch (method) {
      case HttpMethod.GET:
        return await HttpUtil.getDioInstance()
            .get(url, queryParameters: queryParams);
      case HttpMethod.PUT:
        return await HttpUtil.getDioInstance()
            .put(url, queryParameters: queryParams, data: data);
      case HttpMethod.POST:
        return await HttpUtil.getDioInstance()
            .post(url, queryParameters: queryParams, data: data);
      case HttpMethod.PATCH:
        return await HttpUtil.getDioInstance()
            .patch(url, queryParameters: queryParams, data: data);
      case HttpMethod.DELETE:
        return await HttpUtil.getDioInstance()
            .delete(url, queryParameters: queryParams, data: data);
      default:
        EasyLoading.showError('請求方式錯誤');
    }
  } on DioError catch (e) {
    EasyLoading.showError(e.message);
  } on Exception catch (e) {
    EasyLoading.showError(e.toString());
  }

  return null;
}
複製程式碼

這個時候,再來介紹某一項能力就輕鬆多了。

static Future get(String url, {Map<String, dynamic> queryParams}) async {
    return await sendRequest(HttpMethod.GET, url, queryParams: queryParams);
  }

static Future put(String url,
    {Map<String, dynamic> queryParams, dynamic data}) async {
  return await sendRequest(HttpMethod.PUT, url,
      queryParams: queryParams, data: data);
}

static Future post(String url,
    {Map<String, dynamic> queryParams, dynamic data}) async {
  return await sendRequest(HttpMethod.POST, url,
      queryParams: queryParams, data: data);
}

static Future patch(String url,
    {Map<String, dynamic> queryParams, dynamic data}) async {
  return await sendRequest(HttpMethod.PATCH, url,
      queryParams: queryParams, data: data);
}

static Future delete(String url,
    {Map<String, dynamic> queryParams, dynamic data}) async {
  return await sendRequest(HttpMethod.DELETE, url,
      queryParams: queryParams, data: data);
}

static Future uploadSingle(String url, String fileKey, File file,
    {Map<String, dynamic> queryParams}) async {
  FormData formData = FormData.fromMap({
    fileKey: await MultipartFile.fromFile(file.path),
  });
  return await sendRequest(HttpMethod.POST, url,
      queryParams: queryParams, data: formData);
}
複製程式碼

image.png

外面的世界

一切就緒,小王開始切斷了女朋友 Dio 與外界的聯絡,從此外面的世界的人只知道小王有個女朋友,代號叫 HttpUtil,至於到底是誰,長得怎麼樣,全靠小王的一張嘴了!比如DynamicService這位同學:

import '../utils/http_util.dart';

class DynamicService {
  static String host = 'http://localhost:3900/api/';
  static Future list(page, pageSize) async {
    var result = await HttpUtil.get(
      host + 'dynamics',
      queryParams: {'page': page, 'pageSize': pageSize},
    );

    return result;
  }

  static Future get(String id) async {
    var result = await HttpUtil.get(
      host + 'dynamics/' + id,
    );

    return result;
  }

  static Future post(Map<String, dynamic> data) async {
    var result = await HttpUtil.post(host + 'dynamics', data: data);

    return result;
  }

  static Future updateAll(String id, Map<String, dynamic> data) async {
    var result = await HttpUtil.put(host + 'dynamics/' + id, data: data);

    return result;
  }

  static Future update(String id, Map<String, dynamic> data) async {
    var result = await HttpUtil.patch(host + 'dynamics/' + id, data: data);

    return result;
  }

  static Future updateViewCount(String id) async {
    var result = await HttpUtil.patch(host + 'dynamics/view/' + id);

    return result;
  }

  static Future delete(String id) async {
    var result = await HttpUtil.delete(
      host + 'dynamics/' + id,
    );

    return result;
  }
}
複製程式碼

再比如 DynamicAdd 這位同學:

//...
var response = await DynamicService.post(newFormData);
if (response != null && response.statusCode == 200) {
  Dialogs.showInfo(context, '新增成功');
  GetIt.instance
      .get<DynamicListener>()
      .dynamicAdded(DynamicEntity.fromJson(response.data));
  Navigator.of(context).pop();
} else {
  Dialogs.showInfo(this.context,
      response?.statusMessage != null ? response.statusMessage : '新增失敗');
}
//...
複製程式碼

結局

小王給外面人的形象是愈發神祕了,大家都想見見小王的能幹女友,可是每次小王講的都是 HttpUtil,而他自己呢也很滿意自己的這個“金屋藏嬌”的主意,直到有一天他成為老王后,他家的兒子——新的小王給他捅了簍子。

——未完待續

image.png

知識點梳理

  • 封裝:對於第三方外掛,我們不排除以後可能會更換,因此最好的方式是將第三方外掛進行封裝,隱藏具體的實現細節,這樣假設我們以後需要更換外掛,只需要該封裝類即可,而不是讓第三方外掛散落在各個模組裡,這樣改起來非常痛苦,而且容易遺漏。——譬如我們將之前的網路請求程式碼更改,就需要一個個檔案修改。
  • async/await 補充:呼叫網路請求時,使用 await 會等待結果返回,如果不使用await的話,會出現非同步執行情況,結果就是異常捕獲程式碼不起作用。大家可以試一下,在HttpUtil取消 await後,將後臺服務停止的效果。

原始碼已上傳至Dio 封裝原始碼

相關文章