高質量App的架構設計與思考!

最近在作一功能不大、業務也不復雜的小衆App,以往作App是發現本身歷來沒有考慮過一些架構方面的問題,只是按照本身以往的習慣去寫代碼,忽略了App的設計。本次分享主要包含一些開發App的小經驗和技巧,來一次App開發與設計的分享。前端

先和分享下一下實體類的設計與組織形式java

實體類的組織

在作App開發的時候有不少的實體類,項目越複雜實體類就會越多,通過個人一番思考大體這能夠將實體分爲如下幾大數:數據庫

  • 面向數據庫的
  • 服務端返回的數據實體
  • 用於渲染View的實體(使用Databinding)

通常狀況下實體類的操做會通過如下步驟:編程

  1. App請求服務器獲取數據
  2. 將數據存入數據庫(可選)
  3. 渲染頁面展現數據

21

如今的實體的產生只用在請求服務器數據的時候才須要新建,後續的數據庫、頁面渲染實際上是可使用一套實體:json

22

先不說這樣作的行不行,首先三個地方使用同一實體就會引發字段歧義好比服務器數據有Id、本地數據也有Id,那兩個id字段就有衝突了不起不改字段名。後端

另外一種狀況渲染和數據自己並不會一一對應,有時候後端數據給的是一個純數字而前端頁面顯示的是字符串兩個都對應不上,強行放在一塊兒會起來更多的問題。服務器

所爲實體類的的正確組織形式應該是:相互隔離、互不干擾微信

23

數據實體的在渲染以前都須要準備好,好比在ViewModel中將int型的數據轉換成文本型的數據而後再使用Databinding+頁面渲染實體來渲染頁面。網絡

優雅的處理網絡數據

如今Android開發使用的網絡庫大部分都是Okhttp + Retrofit,使用Retrofit網絡交互變的很是簡單一個Service接口就能搞定一切,美茲茲~~,如今大部分後端返回的數據都會是如下形式:架構

{
    "code":0,
    "data": {},
    "msg": ""
}

雖然不能涵蓋全部,但仍是能夠很是讚的數據、消息、成功與否啥都有!對於前面主要是關注data字段,其餘msgcode等都屬於輔助字段。前端對應的實體對象應該是這樣的(假代碼):

public class ApiResponse<T> {
    private int code;
    private T data;
    private String msg;
}

對應的Service那就得定義成這樣(使用了RxJava):

public intface UserService {
    @GET("/xx/{id}")
    Single<ApiResponse<UserInfo> getUserInfoById(@Path("id") Long userId);
}

從接口中能夠看出來,方法的返回值就包了幾層,若是要拿data字段須要通過:ApiResponse -> UserInfo,並且在拿以前還要判斷code字段:

...

if(ApiResponse.code == 0){
    UserInfo info = ApiResponse.getData();
}

...

爲了消除這些冗餘的代碼可使用CallAdapter來使Service方法返回的數據直接就是實體類:

public intface UserService {
    @GET("/xx/{id}")
    Single<UserInfo> getUserInfoById(@Path("id") Long userId);
}

CallAdapter的代碼就不貼了,能夠自行查找。這樣作帶來的另一個問題就是業務代碼如何判斷接口是否成功或失敗,前端必需友好的把錯誤提示給用戶而不是一直搞個Loading在那裏瞎轉~~。現階段最方便的的錯誤傳遞方式是使用Java異常,前端能夠定義業務異常網絡異常

public class BizException extends RuntimeException {
    ...
}

CallAdapter中檢查ApiResponse的返回值是否成功:

if(!ApiResponse != 0){
    throw new BizExcepiton(ApiResponse);
}

若是後端返回業務異常那前端就對應拋出一個BizExcepiton,若是是http錯誤如:40四、400那能夠拋出HttpException。除了BizExcepitonHttpException外還可以使用特定的異常好比後端返回密碼錯誤異常:

public class InvalidPasswordException extends BizException {
    ...
}

如需特殊處理,也能夠知足要求。

健壯的數據層

如今不少應用都開發使用MVVM開發模式數據層都使用Repository來表示,面向數據驅動的開發模式,頁面變化都須要隨着數據變動而更新,數據發生變化而後頁面再作出響應。Repository的拆分要細一點,不建議簡單的弄個UserRepository包含登錄、註冊、更新密碼等等操做,設計Repository的一些想法:

  1. 面向接口編程
  2. 保持單一原則
  3. 功能邊界要清晰(如:登錄、註冊能夠分開)
  4. 業務邏輯儘量的少(複雜的業務考慮Presenter)

一個判斷是不是好的設計的辦法能夠這樣:一個登錄頁面從Activive/Fragment到ViewModel再到Repository,有沒有多餘的代碼。好比上面說的UserRepository包含登錄、註冊可是在一個登錄頁面就不須要有註冊功能,從登錄頁面上來看註冊的代碼就是多餘的(有些App登錄/註冊在一個頁面的~~)。

一個包含登錄、註冊的UserRepository簡單圖:

另一點是儘可能將repository使用到的一些東西集中管理,可引入一個基礎的repository:

public class SimpleRepository {
    
    protected final  <T> T getService(Class<T> clz){
        return Services.getService(clz);
    }
}

作爲SimpleRepository的子類,就不須要考慮從哪裏獲取service的問題。

簡潔的UI層

UI層面能夠分爲ViewModel和View(Activity/Fragment), View的職責應當只有二點:

  1. 展現業務數據
  2. 收集業務數據

例如一些數據的組織、判斷都不該該出如今View中好比:

if (Strings.isNullOrEmpty(phone)) {
       ...
        return;
 }

 if (Strings.isNullOrEmpty(pwd)) {
        ...
        return;
  }

像上面這類的代碼都不該該出如今View中,而在放置在ViewModel裏面,View只收集用戶數據傳遞給ViewModel由它來進行數據校驗。再好比像這樣的if/else代碼也應該放置在ViewModel中:

int age = 10;
 String desc = "";
 if(age < 18){
    desc = "青年";
 }else if(age < 29){
    desc = "中年";
 }

若是數據的顯示和數據的收集過多,建議使用Databinding來進行雙向綁定數據。再搭配LiveData使View做爲觀察者實時監聽數據變化:

registerViewModel.getRegistryResult().observe(this, new SimpleObserver<RegistryInfo>(this));

一旦數據發生變化LiveData就會通知Observer更新,經過DataBinding更新各個頁面數據。

再說ViewModel應該只包含一些簡單的判斷、檢查、打通數據的代碼,若是業務過於複雜能夠考慮加Presetner,若是真的超級複雜那能夠反思下這個複雜的邏輯應不該該放在前端,能不能放在後端呢?

<br> <br>

歡迎關注微信公衆號《架構文摘》,高質量技術文章第一時間推送。

相關文章
相關標籤/搜索