精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Android架構(gòu)模式飛速演進(jìn) 到底哪一個才是自己最需要的?

OSC開源社區(qū) ? 來源:vivo互聯(lián)網(wǎng)技術(shù) ? 2023-08-02 10:23 ? 次閱讀

Android架構(gòu)模式飛速演進(jìn),目前已經(jīng)有MVC、MVP、MVVM、MVI。到底哪一個才是自己業(yè)務(wù)場景最需要的,不深入理解的話是無法進(jìn)行選擇的。這篇文章就針對這些架構(gòu)模式逐一解讀。重點(diǎn)會介紹Compose為什么要結(jié)合MVI進(jìn)行使用。希望知其然,然后找到適合自己業(yè)務(wù)的架構(gòu)模式

一、前言

不得不感嘆,近些年android的架構(gòu)演進(jìn)速度真的是飛快,拿筆者工作這幾年接觸的架構(gòu)來說,就已經(jīng)有了MVC、MVP、MVVM。正當(dāng)筆者準(zhǔn)備把MVVM應(yīng)用到自己項(xiàng)目當(dāng)中時,發(fā)現(xiàn)谷歌悄悄的更新了開發(fā)者文檔(應(yīng)用架構(gòu)指南 | Android 開發(fā)者 | Android Developers (google.cn))。這是一篇指導(dǎo)如何使用MVI的文章。那么這個文章到底為什么更新,想要表達(dá)什么?里面提到的Compose又是什么?難道現(xiàn)在已經(jīng)有的MVC、MVP、MVVM不夠用嗎?MVI跟已有的這些架構(gòu)又有什么不同之處呢?

有人會說,不管什么架構(gòu),都是圍繞著“解耦”來實(shí)現(xiàn)的,這種說法是正確的,但是耦合度高只是現(xiàn)象,采用什么手段降低耦合度?降低耦合度之后的程序方便單元測試嗎?如果我在MVC、MVP、MVVM的基礎(chǔ)上做解耦,可以做的很徹底嗎?

先告訴你答案, MVC、MVP、MVVM無法做到徹底的解耦,但是MVI+Compose可以做到徹底的解耦,也就是本文的重點(diǎn)講解部分。本文結(jié)合具體的代碼和案例,復(fù)雜問題簡單化,并且結(jié)合較多技術(shù)博客做了統(tǒng)一的總結(jié),相信你讀完會收獲頗豐。

那么本篇文章編寫的意義,就是為了能夠深入淺出的講解MVI+Compose,大家可以先試想下這樣的業(yè)務(wù)場景,如果是你,你會選擇哪種架構(gòu)實(shí)現(xiàn)?

業(yè)務(wù)場景考慮

使用手機(jī)號進(jìn)行登錄

登錄完之后驗(yàn)證是否指定的賬號A

如果是賬號A,則進(jìn)行點(diǎn)贊操作

上面三個步驟是順序執(zhí)行的,手機(jī)號的登錄、賬號的驗(yàn)證、點(diǎn)贊都是與服務(wù)端進(jìn)行交互之后,獲取對應(yīng)的返回結(jié)果,然后再做下一步。

在開始介紹MVI+Compose之前,需要循序漸進(jìn),了解每個架構(gòu)模式的缺點(diǎn),才知道為什么Google提出MVI+Compose。

正式開始前,按照架構(gòu)模式的提出時間來看下是如何演變的,每個模式的提出往往不是基于android提出,而是基于服務(wù)端或者前端演進(jìn)而來,這也說明設(shè)計(jì)思路上都是大同小異的:

1fce2a7e-305d-11ee-9e74-dac502259ad0.jpg

二、架構(gòu)模式過去式?

2.1MVC已經(jīng)存在很久了

MVC模式提出時間太久了,早在1978年就被提出,所以一定不是用于android,android的MVC架構(gòu)主要還是源于服務(wù)端的SpringMVC,在2007年到2017年之間,MVC占據(jù)著主導(dǎo)地位,目前我們android中看到的MVC架構(gòu)模式是這樣的。

MVC架構(gòu)這幾個部分的含義如下,網(wǎng)上隨便找找就有一堆說明。

MVC架構(gòu)分為以下幾個部分

【模型層Model】:主要負(fù)責(zé)網(wǎng)絡(luò)請求,數(shù)據(jù)庫處理,I/O的操作,即頁面的數(shù)據(jù)來源

【視圖層View】:對應(yīng)于xml布局文件和java代碼動態(tài)view部分

【控制層Controller】:主要負(fù)責(zé)業(yè)務(wù)邏輯,在android中由Activity承擔(dān)

(1)MVC代碼示例

我們舉個登錄驗(yàn)證的例子來看下MVC架構(gòu)一般怎么實(shí)現(xiàn)。

這個是controller

MVC架構(gòu)實(shí)現(xiàn)登錄流程-controller

public class MvcLoginActivity extends AppCompatActivity {
    private EditText userNameEt;
    private EditText passwordEt;
    private User user;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvc_login);
 
        user = new User();
        userNameEt = findViewById(R.id.user_name_et);
        passwordEt = findViewById(R.id.password_et);
        Button loginBtn = findViewById(R.id.login_btn);
 
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LoginUtil.getInstance().doLogin(userNameEt.getText().toString(), passwordEt.getText().toString(), new LoginCallBack() {
                    @Override
                    public void loginResult(@NonNull com.example.mvcmvpmvvm.mvc.Model.User success) {
                        if (null != user) {
                            // 這里免不了的,會有業(yè)務(wù)處理
                            //1、保存用戶賬號
                            //2、loading消失
                            //3、大量的變量判斷
                            //4、再做進(jìn)一步的其他網(wǎng)絡(luò)請求
                            Toast.makeText(MvcLoginActivity.this, " Login Successful",
                                            Toast.LENGTH_SHORT)
                                    .show();
                        } else {
                            Toast.makeText(MvcLoginActivity.this,
                                            "Login Failed",
                                            Toast.LENGTH_SHORT)
                                    .show();
                        }
                    }
                });
            }
        });
    }
 
}

這個是model

MVC架構(gòu)實(shí)現(xiàn)登錄流程-model

public class LoginService {
 
    public static LoginUtil getInstance() {
        return new LoginUtil();
    }
 
    public void doLogin(String userName, String password, LoginCallBack loginCallBack) {
        User user = new User();
        if (userName.equals("123456") && password.equals("123456")) {
            user.setUserName(userName);
            user.setPassword(password);
            loginCallBack.loginResult(user);
        } else {
            loginCallBack.loginResult(null);
        }
    }
}

例子很簡單,主要做了下面這些事情

寫一個專門的工具類LoginService,用來做網(wǎng)絡(luò)請求doLogin,驗(yàn)證登錄賬號是否正確,然后把驗(yàn)證結(jié)果返回。

activity調(diào)用LoginService,并且把賬號信息傳遞給doLogin方法,當(dāng)獲取到結(jié)果后,進(jìn)行對應(yīng)的業(yè)務(wù)操作。

(2)MVC優(yōu)缺點(diǎn)

MVC在大部分簡單業(yè)務(wù)場景下是夠用的,主要優(yōu)點(diǎn)如下:

結(jié)構(gòu)清晰,職責(zé)劃分清晰

降低耦合

有利于組件重用

但是隨著時間的推移,你的MVC架構(gòu)可能慢慢的演化成了下面的模式。拿上面的例子來說,你只做登錄比較簡單,但是當(dāng)你的頁面把登錄賬號校驗(yàn)、點(diǎn)贊都實(shí)現(xiàn)的時候,方法會比較多,共享一個view的時候,或者共同操作一個數(shù)據(jù)源的時候,就會出現(xiàn)變量滿天飛,view四處被調(diào)用,相信大家也深有體會。

1fe41672-305d-11ee-9e74-dac502259ad0.jpg

不可避免的,MVC就存在了下面的問題

歸根究底,在android里面使用MVC的時候,對于Model、View、Controller的劃分范圍,總是那么的不明確,因?yàn)楸旧硭麄冎g就有無法直接分割的依賴關(guān)系。所以總是避免不了這樣的問題:

View與Model之間還存在依賴關(guān)系,甚至有時候?yàn)榱藞D方便,把Model和View互傳,搞得View和Model耦合度極高,低耦合是面向?qū)ο笤O(shè)計(jì)標(biāo)準(zhǔn)之一,對于大型項(xiàng)目來說,高耦合會很痛苦,這在開發(fā)、測試,維護(hù)方面都需要花大量的精力。

那么在Controller層,Activity有時既要管理View,又要控制與用戶的交互,充當(dāng)Controller,可想而知,當(dāng)稍微有不規(guī)范的寫法,這個Activity就會很復(fù)雜,承擔(dān)的功能會越來越多。

花了一定篇幅介紹MVC,是讓大家對MVC中Model、View、Controller應(yīng)該各自完成什么事情能深入理解,這樣才有后面架構(gòu)不斷演進(jìn)的意義。

2.2 MVP架構(gòu)的由來

(1)MVP要解決什么問題?

2016年10月, Google官方提供了MVP架構(gòu)的Sample代碼來展示這種模式的用法,成為最流行的架構(gòu)。

相對于MVC,MVP將Activity復(fù)雜的邏輯處理移至另外的一個類(Presenter)中,此時Activity就是MVP模式中的View,它負(fù)責(zé)UI元素的初始化,建立UI元素與Presenter的關(guān)聯(lián)(Listener之類),同時自己也會處理一些簡單的邏輯(復(fù)雜的邏輯交由 Presenter處理)。

那么MVP 同樣將代碼劃分為三個部分:

結(jié)構(gòu)說明

View:對應(yīng)于Activity與XML,只負(fù)責(zé)顯示UI,只與Presenter層交互,與Model層沒有耦合;

Model: 負(fù)責(zé)管理業(yè)務(wù)數(shù)據(jù)邏輯,如網(wǎng)絡(luò)請求、數(shù)據(jù)庫處理;

Presenter:負(fù)責(zé)處理大量的邏輯操作,避免Activity的臃腫。

來看看MVP的架構(gòu)圖:

1ff865c8-305d-11ee-9e74-dac502259ad0.jpg

與MVC的最主要區(qū)別

View與Model并不直接交互,而是通過與Presenter交互來與Model間接交互。而在MVC中View可以與Model直接交互。

通常View與Presenter是一對一的,但復(fù)雜的View可能綁定多個Presenter來處理邏輯。而Controller回歸本源,首要職責(zé)是加載應(yīng)用的布局和初始化用戶界面,并接受并處理來自用戶的操作請求,它是基于行為的,并且可以被多個View共享,Controller可以負(fù)責(zé)決定顯示哪個View。

Presenter與View的交互是通過接口來進(jìn)行的,更有利于添加單元測試。

(2)MVP代碼示意

① 先來看包結(jié)構(gòu)圖

20079ba6-305d-11ee-9e74-dac502259ad0.png

②建立Bean

MVP架構(gòu)實(shí)現(xiàn)登錄流程-model

public class User {
    private String userName;
    private String password;
 
    public String getUserName() {
        return ...
    }
 
    public void setUserName(String userName) {
        ...;
    }
 
}

③建立Model接口 (處理業(yè)務(wù)邏輯,這里指數(shù)據(jù)讀寫),先寫接口方法,后寫實(shí)現(xiàn)

MVP架構(gòu)實(shí)現(xiàn)登錄流程-model

public interface IUserBiz {
    boolean login(String userName, String password);
}

④建立presenter(主導(dǎo)器,通過iView和iModel接口操作model和view),activity可以把所有邏輯給presenter處理,這樣java邏輯就從activity中分離出來。

MVP架構(gòu)實(shí)現(xiàn)登錄流程-model

public class LoginPresenter{
    private UserBiz userBiz;
    private IMvpLoginView iMvpLoginView;
 
    public LoginPresenter(IMvpLoginView iMvpLoginView) {
        this.iMvpLoginView = iMvpLoginView;
        this.userBiz = new UserBiz();
    }
 
    public void login() {
        String userName = iMvpLoginView.getUserName();
        String password = iMvpLoginView.getPassword();
        boolean isLoginSuccessful = userBiz.login(userName, password);
        iMvpLoginView.onLoginResult(isLoginSuccessful);
    }
}

⑤View視圖建立view,用于更新ui中的view狀態(tài),這里列出需要操作當(dāng)前view的方法,也是接口IMvpLoginView

MVP架構(gòu)實(shí)現(xiàn)登錄流程-model

public interface IMvpLoginView {
    String getUserName();
 
    String getPassword();
 
    void onLoginResult(Boolean isLoginSuccess);
}

⑥ activity中實(shí)現(xiàn)IMvpLoginView接口,在其中操作view,實(shí)例化一個presenter變量。

MVP架構(gòu)實(shí)現(xiàn)登錄流程-model

public class MvpLoginActivity extends AppCompatActivity implements IMvpLoginView{
    private EditText userNameEt;
    private EditText passwordEt;
    private LoginPresenter loginPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_login);
 
        userNameEt = findViewById(R.id.user_name_et);
        passwordEt = findViewById(R.id.password_et);
        Button loginBtn = findViewById(R.id.login_btn);
 
        loginPresenter = new LoginPresenter(this);
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginPresenter.login();
            }
        });
    }
 
    @Override
    public String getUserName() {
        return userNameEt.getText().toString();
    }
 
    @Override
    public String getPassword() {
        return passwordEt.getText().toString();
    }
 
    @Override
    public void onLoginResult(Boolean isLoginSuccess) {
        if (isLoginSuccess) {
            Toast.makeText(MvpLoginActivity.this,
                    getUserName() + " Login Successful",
                    Toast.LENGTH_SHORT)
                    .show();
        } else {
            Toast.makeText(MvpLoginActivity.this,
                    "Login Failed",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

(3)MVP優(yōu)缺點(diǎn)

因此,Activity及從MVC中的Controller中解放出來了,這會Activity主要做顯示View的作用和用戶交互。每個Activity可以根據(jù)自己顯示View的不同實(shí)現(xiàn)View視圖接口IUserView。

通過對比同一實(shí)例的MVC與MVP的代碼,可以證實(shí)MVP模式的一些優(yōu)點(diǎn):

在MVP中,Activity的代碼不臃腫;

在MVP中,Model(IUserModel的實(shí)現(xiàn)類)的改動不會影響Activity(View),兩者也互不干涉,而在MVC中會;

在MVP中,IUserView這個接口可以實(shí)現(xiàn)方便地對Presenter的測試;

在MVP中,UserPresenter可以用于多個視圖,但是在MVC中的Activity就不行。

但還是存在一些缺點(diǎn):

雙向依賴:View 和 Presenter 是雙向依賴的,一旦 View 層做出改變,相應(yīng)地 Presenter 也需要做出調(diào)整。在業(yè)務(wù)語境下,View 層變化是大概率事件;

內(nèi)存泄漏風(fēng)險:Presenter 持有 View 層的引用,當(dāng)用戶關(guān)閉了 View 層,但 Model 層仍然在進(jìn)行耗時操作,就會有內(nèi)存泄漏風(fēng)險。雖然有解決辦法,但還是存在風(fēng)險點(diǎn)和復(fù)雜度(弱引用 / onDestroy() 回收 Presenter)。

三、MVVM其實(shí)夠用了

3.1MVVM思想存在很久了

MVVM最初是在2005年由微軟提出的一個UI架構(gòu)概念。后來在2015年的時候,開始應(yīng)用于android中。

MVVM 模式改動在于中間的 Presenter 改為 ViewModel,MVVM 同樣將代碼劃分為三個部分:

View:Activity 和 Layout XML 文件,與 MVP 中 View 的概念相同;

Model:負(fù)責(zé)管理業(yè)務(wù)數(shù)據(jù)邏輯,如網(wǎng)絡(luò)請求、數(shù)據(jù)庫處理,與 MVP 中 Model 的概念相同;

ViewModel:存儲視圖狀態(tài),負(fù)責(zé)處理表現(xiàn)邏輯,并將數(shù)據(jù)設(shè)置給可觀察數(shù)據(jù)容器。

與MVP唯一的區(qū)別是,它采用雙向數(shù)據(jù)綁定(data-binding):View的變動,自動反映在 ViewModel,反之亦然。

MVVM架構(gòu)圖如下所示:

201a193e-305d-11ee-9e74-dac502259ad0.jpg

可以看出MVVM與MVP的主要區(qū)別在于,你不用去主動去刷新UI了,只要Model數(shù)據(jù)變了,會自動反映到UI上。換句話說,MVVM更像是自動化的MVP。

MVVM的雙向數(shù)據(jù)綁定主要通過DataBinding實(shí)現(xiàn),但是大部分人應(yīng)該跟我一樣,不使用DataBinding,那么大家最終使用的MVVM架構(gòu)就變成了下面這樣:

202e6c4a-305d-11ee-9e74-dac502259ad0.jpg

總結(jié)一下:

實(shí)際使用MVVM架構(gòu)說明

View觀察ViewModel的數(shù)據(jù)變化并自我更新,這其實(shí)是單一數(shù)據(jù)源而不是雙向數(shù)據(jù)綁定,所以MVVM的雙向綁定這一大特性我這里并沒有用到

View通過調(diào)用ViewModel提供的方法來與ViewMdoel交互。

3.2 MVVM代碼示例

(1)建立viewModel,并且提供一個可供view調(diào)取的方法 login(String userName,String

password)

MVVM架構(gòu)實(shí)現(xiàn)登錄流程-model

public class LoginViewModel extends ViewModel {
    private User user;
    private MutableLiveData isLoginSuccessfulLD;
 
    public LoginViewModel() {
        this.isLoginSuccessfulLD = new MutableLiveData<>();
        user = new User();
    }
 
    public MutableLiveData getIsLoginSuccessfulLD() {
        return isLoginSuccessfulLD;
    }
 
    public void setIsLoginSuccessfulLD(boolean isLoginSuccessful) {
        isLoginSuccessfulLD.postValue(isLoginSuccessful);
    }
 
    public void login(String userName, String password) {
        if (userName.equals("123456") && password.equals("123456")) {
            user.setUserName(userName);
            user.setPassword(password);
            setIsLoginSuccessfulLD(true);
        } else {
            setIsLoginSuccessfulLD(false);
        }
    }
 
    public String getUserName() {
        return user.getUserName();
    }
}

(2)在activity中聲明viewModel,并建立觀察。點(diǎn)擊按鈕,觸發(fā) login(String userName, String password)。持續(xù)作用的觀察者loginObserver。只要LoginViewModel 中的isLoginSuccessfulLD變化,就會對應(yīng)的有響應(yīng)

MVVM架構(gòu)實(shí)現(xiàn)登錄流程-model

public class MvvmLoginActivity extends AppCompatActivity {
    private LoginViewModel loginVM;
    private EditText userNameEt;
    private EditText passwordEt;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvvm_login);
 
        userNameEt = findViewById(R.id.user_name_et);
        passwordEt = findViewById(R.id.password_et);
        Button loginBtn = findViewById(R.id.login_btn);
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginVM.login(userNameEt.getText().toString(), passwordEt.getText().toString());
            }
        });
 
        loginVM = new ViewModelProvider(this).get(LoginViewModel.class);
        loginVM.getIsLoginSuccessfulLD().observe(this, loginObserver);
    }
 
    private Observer loginObserver = new Observer() {
        @Override
        public void onChanged(@Nullable Boolean isLoginSuccessFul) {
            if (isLoginSuccessFul) {
                Toast.makeText(MvvmLoginActivity.this, "登錄成功",
                        Toast.LENGTH_SHORT)
                        .show();
            } else {
                Toast.makeText(MvvmLoginActivity.this,
                        "登錄失敗",
                        Toast.LENGTH_SHORT)
                        .show();
            }
        }
    };
}

3.3MVVM優(yōu)缺點(diǎn)

通過上面的代碼,可以總結(jié)出MVVM的優(yōu)點(diǎn):

在實(shí)現(xiàn)細(xì)節(jié)上,View 和 Presenter 從雙向依賴變成 View 可以向 ViewModel 發(fā)指令,但 ViewModel 不會直接向 View 回調(diào),而是讓 View 通過觀察者的模式去監(jiān)聽數(shù)據(jù)的變化,有效規(guī)避了 MVP 雙向依賴的缺點(diǎn)。

但 MVVM 在某些情況下,也存在一些缺點(diǎn):

(1)關(guān)聯(lián)性比較強(qiáng)的流程,liveData太多,并且理解成本較高

當(dāng)業(yè)務(wù)比較復(fù)雜的時候,在viewModel中必然存在著比較多的LiveData去管理。當(dāng)然,如果你去管理好這些LiveData,讓他們?nèi)ヌ幚順I(yè)務(wù)流程,問題也不大,只不過理解的成本會高些。

(2)不便于單元測試

viewModel里面一般都是對數(shù)據(jù)庫和網(wǎng)絡(luò)數(shù)據(jù)進(jìn)行處理,包含了業(yè)務(wù)邏輯在里面,當(dāng)要去對某一流程進(jìn)行測試時,并沒有辦法完全剝離數(shù)據(jù)邏輯的處理流程,單元測試也就增加了難度。

那么我們來看看缺點(diǎn)對應(yīng)的具體場景是什么,便于我們后續(xù)進(jìn)一步探討MVI架構(gòu)。

(1)在上面登錄之后,需要驗(yàn)證賬號信息,然后再自動進(jìn)行點(diǎn)贊。那么,viewModel里面對應(yīng)的增加幾個方法,每個方法對應(yīng)一個LiveData

MVVM架構(gòu)實(shí)現(xiàn)登錄流程-model

public class LoginMultiViewModel extends ViewModel {
    private User user;
    // 是否登錄成功
    private MutableLiveData isLoginSuccessfulLD;
    // 是否為指定賬號
    private MutableLiveData isMyAccountLD;
    // 如果是指定賬號,進(jìn)行點(diǎn)贊
    private MutableLiveData goThumbUp;
 
    public LoginMultiViewModel() {
        this.isLoginSuccessfulLD = new MutableLiveData<>();
        this.isMyAccountLD = new MutableLiveData<>();
        this.goThumbUp = new MutableLiveData<>();
        user = new User();
    }
 
    public MutableLiveData getIsLoginSuccessfulLD() {
        return isLoginSuccessfulLD;
    }
 
    public MutableLiveData getIsMyAccountLD() {
        return isMyAccountLD;
    }
 
    public MutableLiveData getGoThumbUpLD() {
        return goThumbUp;
    }
 
   ...
 
    public void login(String userName, String password) {
        if (userName.equals("123456") && password.equals("123456")) {
            user.setUserName(userName);
            user.setPassword(password);
            setIsLoginSuccessfulLD(true);
        } else {
            setIsLoginSuccessfulLD(false);
        }
    }
 
    public void isMyAccount(@NonNull String userName) {
        try {
            Thread.sleep(1000);
        } catch (Exception ex) {
 
        }
        if (userName.equals("123456")) {
            setIsMyAccountSuccessfulLD(true);
        } else {
            setIsMyAccountSuccessfulLD(false);
        }
    }
 
    public void goThumbUp(boolean isMyAccount) {
        setGoThumbUpLD(isMyAccount);
    }
 
    public String getUserName() {
        return user.getUserName();
    }
}

(2)再來看看你可能使用的一種處理邏輯,在判斷登錄成功之后,使用變量isLoginSuccessFul再去做 loginVM.isMyAccount(userNameEt.getText().toString());在賬號驗(yàn)證成功之后,再去通過變量isMyAccount去做loginVM.goThumbUp(true);

MVVM架構(gòu)實(shí)現(xiàn)登錄流程-model

public class MvvmFaultLoginActivity extends AppCompatActivity {
    private LoginMultiViewModel loginVM;
    private EditText userNameEt;
    private EditText passwordEt;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvvm_fault_login);
 
        userNameEt = findViewById(R.id.user_name_et);
        passwordEt = findViewById(R.id.password_et);
        Button loginBtn = findViewById(R.id.login_btn);
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginVM.login(userNameEt.getText().toString(), passwordEt.getText().toString());
            }
        });
 
        loginVM = new ViewModelProvider(this).get(LoginMultiViewModel.class);
        loginVM.getIsLoginSuccessfulLD().observe(this, loginObserver);
        loginVM.getIsMyAccountLD().observe(this, isMyAccountObserver);
        loginVM.getGoThumbUpLD().observe(this, goThumbUpObserver);
    }
 
    private Observer loginObserver = new Observer() {
        @Override
        public void onChanged(@Nullable Boolean isLoginSuccessFul) {
            if (isLoginSuccessFul) {
                Toast.makeText(MvvmFaultLoginActivity.this, "登錄成功,開始校驗(yàn)賬號", Toast.LENGTH_SHORT).show();
                loginVM.isMyAccount(userNameEt.getText().toString());
            } else {
                Toast.makeText(MvvmFaultLoginActivity.this,
                        "登錄失敗",
                        Toast.LENGTH_SHORT)
                        .show();
            }
        }
    };
 
    private Observer isMyAccountObserver = new Observer() {
        @Override
        public void onChanged(@Nullable Boolean isMyAccount) {
 
            if (isMyAccount) {
                Toast.makeText(MvvmFaultLoginActivity.this, "校驗(yàn)成功,開始點(diǎn)贊", Toast.LENGTH_SHORT).show();
                loginVM.goThumbUp(true);
            }
        }
    };
 
    private Observer goThumbUpObserver = new Observer() {
        @Override
        public void onChanged(@Nullable Boolean isThumbUpSuccess) {
            if (isThumbUpSuccess) {
                Toast.makeText(MvvmFaultLoginActivity.this,
                                "點(diǎn)贊成功",
                                Toast.LENGTH_SHORT)
                        .show();
            } else {
                Toast.makeText(MvvmFaultLoginActivity.this,
                                "點(diǎn)贊失敗",
                                Toast.LENGTH_SHORT)
                        .show();
            }
        }
    };
}

毫無疑問,這種交互在實(shí)際開發(fā)中是可能存在的,頁面比較復(fù)雜的時候,這種變量也就滋生了。這種場景,就有必要聊聊MVI架構(gòu)了。

四、MVI有存在的必要性嗎?

4.1 MVI的由來

MVI 模式來源于2014年的 Cycle.js(一個 JavaScript框架),并且在主流的 JS 框架 Redux 中大行其道,然后就被一些大佬移植到了 Android 上(比如最早期用Java寫的 mosby)。

既然MVVM是目前android官方推薦的架構(gòu),又為什么要有MVI呢?其實(shí)應(yīng)用架構(gòu)指南中并沒有提出MVI的概念,而是提到了單向數(shù)據(jù)流,唯一數(shù)據(jù)源,這也是區(qū)別MVVM的特性。

不過還是要說明一點(diǎn),凡是MVI做到的,只要你使用MVVM去實(shí)現(xiàn),基本上也能做得到。只是說在接下來要講的內(nèi)容里面,MVI具備的封裝思路,是可以直接使用的,并且是便于單元測試的。

MVI的思想:靠數(shù)據(jù)驅(qū)動頁面 (其實(shí)當(dāng)你把這種思想應(yīng)用在各個框架的時候,你的那個框架都會更加優(yōu)雅)

MVI架構(gòu)包括以下幾個部分

Model:主要指UI狀態(tài)(State)。例如頁面加載狀態(tài)、控件位置等都是一種UI狀態(tài)。

View: 與其他MVX中的View一致,可能是一個Activity或者任意UI承載單元。MVI中的View通過訂閱Model的變化實(shí)現(xiàn)界面刷新。

Intent: 此Intent不是Activity的Intent,用戶的任何操作都被包裝成Intent后發(fā)送給Model層進(jìn)行數(shù)據(jù)請求。

看下交互流程圖:

204010ee-305d-11ee-9e74-dac502259ad0.jpg

對流程圖做下解釋說明:

(1)用戶操作以Intent的形式通知Model
(2)Model基于Intent更新State。這個里面包括使用ViewModel進(jìn)行網(wǎng)絡(luò)請求,更新State的操作
(3)View接收到State變化刷新UI。

4.2 MVI的代碼示例

直接看代碼吧

(1)先看下包結(jié)構(gòu)

20580b68-305d-11ee-9e74-dac502259ad0.png

(2)用戶點(diǎn)擊按鈕,發(fā)起登錄流程

loginViewModel.loginActionIntent.send(LoginActionIntent.DoLogin(userNameEt.text.toString(), passwordEt.text.toString()))。

此處是發(fā)送了一個Intent出去

MVI架構(gòu)代碼-View

loginBtn.setOnClickListener {
            lifecycleScope.launch {
                loginViewModel.loginActionIntent.send(LoginActionIntent.DoLogin(userNameEt.text.toString(), passwordEt.text.toString()))
            }
 
        }

(3)ViewModel對Intent進(jìn)行監(jiān)聽

initActionIntent()。在這里可以把按鈕點(diǎn)擊事件的Intent消費(fèi)掉

MVI架構(gòu)代碼-Model

class LoginViewModel : ViewModel() {
    companion object {
        const val TAG = "LoginViewModel"
    }
 
    private val _repository = LoginRepository()
    val loginActionIntent = Channel(Channel.UNLIMITED)
    private val _loginActionState = MutableSharedFlow()
 
 
    val state: SharedFlow
        get() = _loginActionState
 
 
    init {
        // 可以用來初始化一些頁面或者參數(shù)
        initActionIntent()
    }
 
    private fun initActionIntent() {
        viewModelScope.launch {
            loginActionIntent.consumeAsFlow().collect {
                when (it) {
                    is LoginActionIntent.DoLogin -> {
                        doLogin(it.username, it.password)
                    }
                    else -> {
 
                    }
                }
            }
        }
    }
 
}

(4)使用respository進(jìn)行網(wǎng)絡(luò)請求,更新state

MVI架構(gòu)代碼-Repository

class LoginRepository {
    suspend fun requestLoginData(username: String, password: String) : Boolean {
 
        delay(1000)
        if (username == "123456" && password == "123456") {
            return true
        }
        return false
    }
 
    suspend fun requestIsMyAccount(username: String, password: String) : Boolean {
 
        delay(1000)
        if (username == "123456") {
            return true
        }
        return false
    }
 
 
    suspend fun requestThumbUp(username: String, password: String) : Boolean {
 
        delay(1000)
        if (username == "123456") {
            return true
        }
        return false
    }
}

MVI架構(gòu)代碼-更新state

private fun doLogin(username: String, password: String) {
        viewModelScope.launch {
            if (username.isEmpty() || password.isEmpty()) {
                return@launch
            }
            // 設(shè)置頁面正在加載
            _loginActionState.emit(LoginActionState.LoginLoading(username, password))
 
            // 開始請求數(shù)據(jù)
            val loginResult = _repository.requestLoginData(username, password)
 
            if (!loginResult) {
                //登錄失敗
                _loginActionState.emit(LoginActionState.LoginFailed(username, password))
                return@launch
            }
            _loginActionState.emit(LoginActionState.LoginSuccessful(username, password))
            //登錄成功繼續(xù)往下
            val isMyAccount = _repository.requestIsMyAccount(username, password)
            if (!isMyAccount) {
                //校驗(yàn)賬號失敗
                _loginActionState.emit(LoginActionState.IsMyAccountFailed(username, password))
                return@launch
            }
            _loginActionState.emit(LoginActionState.IsMyAccountSuccessful(username, password))
            //校驗(yàn)賬號成功繼續(xù)往下
            val isThumbUpSuccess = _repository.requestThumbUp(username, password)
            if (!isThumbUpSuccess) {
                //點(diǎn)贊失敗
                _loginActionState.emit(LoginActionState.GoThumbUpFailed(username, password))
                return@launch
            }
            //點(diǎn)贊成功繼續(xù)往下
            _loginActionState.emit(LoginActionState.GoThumbUpSuccessful(true))
        }
    }

(5)在View中監(jiān)聽state的變化,做頁面刷新

MVI架構(gòu)代碼-Repository

fun observeViewModel() {
        lifecycleScope.launch {
            loginViewModel.state.collect {
                when(it) {
                    is LoginActionState.LoginLoading -> {
                        Toast.makeText(baseContext, "登錄中", Toast.LENGTH_SHORT).show()
                    }
                    is LoginActionState.LoginFailed -> {
                        Toast.makeText(baseContext, "登錄失敗", Toast.LENGTH_SHORT).show()
                    }
                    is LoginActionState.LoginSuccessful -> {
                        Toast.makeText(baseContext, "登錄成功,開始校驗(yàn)賬號", Toast.LENGTH_SHORT).show()
                    }
                    is LoginActionState.IsMyAccountSuccessful -> {
                        Toast.makeText(baseContext, "校驗(yàn)成功,開始點(diǎn)贊", Toast.LENGTH_SHORT).show()
                    }
                    is LoginActionState.GoThumbUpSuccessful -> {
                        resultView.text = "點(diǎn)贊成功"
                        Toast.makeText(baseContext, "點(diǎn)贊成功", Toast.LENGTH_SHORT).show()
                    }
                    else -> {}
                }
 
            }
        }
    }

通過這個流程,可以看到用戶點(diǎn)擊登錄操作,一直到最后刷新頁面,是一個串行的操作。在這種場景下,使用MVI架構(gòu),再合適不過

4.2 MVI的優(yōu)缺點(diǎn)

(1)MVI的優(yōu)點(diǎn)如下:

可以更好的進(jìn)行單元測試

針對上面的案例,使用MVI這種單向數(shù)據(jù)流的形式要比MVVM更加的合適,并且便于單元測試,每個節(jié)點(diǎn)都較為獨(dú)立,沒有代碼上的耦合。

訂閱一個 ViewState 就可以獲取所有狀態(tài)和數(shù)據(jù)

不需要像MVVM那樣管理多個LiveData,可以直接使用一個state進(jìn)行管理,相比 MVVM 是新的特性。

但MVI 本身也存在一些缺點(diǎn):

State 膨脹: 所有視圖變化都轉(zhuǎn)換為 ViewState,還需要管理不同狀態(tài)下對應(yīng)的數(shù)據(jù)。實(shí)踐中應(yīng)該根據(jù)狀態(tài)之間的關(guān)聯(lián)程度來決定使用單流還是多流;

內(nèi)存開銷: ViewState 是不可變類,狀態(tài)變更時需要創(chuàng)建新的對象,存在一定內(nèi)存開銷;

局部刷新: View 根據(jù) ViewState 響應(yīng),不易實(shí)現(xiàn)局部 Diff 刷新,可以使用 Flow#distinctUntilChanged() 來刷新來減少不必要的刷新。

更關(guān)鍵的一點(diǎn),即使單向數(shù)據(jù)流封裝的很多,仍然避免不了來一個新人,不遵守這個單向數(shù)據(jù)流的寫法,隨便去處理view。這時候就要去引用Compose了。

五、不妨利用Compose升級MVI

2021年,谷歌發(fā)布Jetpack Compose1.0,2022年,又更新了文章應(yīng)用架構(gòu)指南,在進(jìn)行界面層的搭建時,建議方案如下:

在屏幕上呈現(xiàn)數(shù)據(jù)的界面元素。您可以使用 View 或 Jetpack Compose 函數(shù)構(gòu)建這些元素。

用于存儲數(shù)據(jù)、向界面提供數(shù)據(jù)以及處理邏輯的狀態(tài)容器(如 ViewModel 類)。

為什么這里會提到Compose?

使用Compose的原因之一

即使你使用了MVI架構(gòu),但是當(dāng)有人不遵守這個設(shè)計(jì)理念時,從代碼層面是無法避免別人使用非MVI架構(gòu),久而久之,導(dǎo)致你的代碼混亂。

意思就是說,你在使用MVI架構(gòu)搭建頁面之后,有個人突然又引入了MVC的架構(gòu),是無法避免的。Compose可以完美解決這個問題。

接下來就是本文與其他技術(shù)博客不一樣的地方,把Compose如何使用,為什么這樣使用做下說明,不要只看理論,最好實(shí)戰(zhàn)。

5.1Compose的主要作用

Compose可以做到界面view在一開始的時候就要綁定數(shù)據(jù)源,從而達(dá)到無法在其他地方被篡改的目的。

怎么理解?

當(dāng)你有個TextView被聲明之后,按照之前的架構(gòu),可以獲取這個TextView,并且給它的text隨意賦值,這就導(dǎo)致了TextView就有可能不止是在MVI架構(gòu)里面使用,也可能在MVC架構(gòu)里面使用。

5.2MVI+Compose的代碼示例

MVI+Compose架構(gòu)代碼

class MviComposeLoginActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        lifecycleScope.launch {
            setContent {
                BoxWithConstraints(
                    modifier = Modifier
                        .background(colorResource(id = R.color.white))
                        .fillMaxSize()
                ) {
                    loginConstraintToDo()
                }
            }
        }
 
    }
 
 
    @Composable
    fun EditorTextField(textFieldState: TextFieldState, label : String, modifier: Modifier = Modifier) {
        // 定義一個可觀測的text,用來在TextField中展示
        TextField(
            value = textFieldState.text, // 顯示文本
            onValueChange = { textFieldState.text = it }, // 文字改變時,就賦值給text
            modifier = modifier,
            label = { Text(text = label) }, // label是Input
            placeholder = @Composable { Text(text = "123456") }, // 不輸入內(nèi)容時的占位符
        )
    }
 
 
    @SuppressLint("CoroutineCreationDuringComposition")
    @Composable
    internal fun  loginConstraintToDo(model: ComposeLoginViewModel = viewModel()){
 
        val state by model.uiState.collectAsState()
        val context = LocalContext.current
 
        loginConstraintLayout(
            onLoginBtnClick = { text1, text2 ->
                lifecycleScope.launch {
                    model.sendEvent(TodoEvent.DoLogin(text1, text2))
                }
            }, state.isThumbUpSuccessful
        )
 
 
        when {
            state.isLoginSuccessful -> {
                Toast.makeText(baseContext, "登錄成功,開始校驗(yàn)賬號", Toast.LENGTH_SHORT).show()
                model.sendEvent(TodoEvent.VerifyAccount("123456", "123456"))
            }
            state.isAccountSuccessful -> {
                Toast.makeText(baseContext, "賬號校驗(yàn)成功,開始點(diǎn)贊", Toast.LENGTH_SHORT).show()
                model.sendEvent(TodoEvent.ThumbUp("123456", "123456"))
            }
            state.isThumbUpSuccessful -> {
                Toast.makeText(baseContext, "點(diǎn)贊成功", Toast.LENGTH_SHORT).show()
            }
        }
 
    }
 
 
    @Composable
    fun loginConstraintLayout(onLoginBtnClick: (String, String) -> Unit, thumbUpSuccessful: Boolean){
        ConstraintLayout() {
            //通過createRefs創(chuàng)建三個引用
            // 初始化聲明兩個元素,如果只聲明一個,則可用 createRef() 方法
            // 這里聲明的類似于 View 的 id
            val (firstText, secondText, button, text) = createRefs()
 
            val firstEditor = remember {
                TextFieldState()
            }
 
            val secondEditor = remember {
                TextFieldState()
            }
 
            EditorTextField(firstEditor,"123456", Modifier.constrainAs(firstText) {
                top.linkTo(parent.top, margin = 16.dp)
                start.linkTo(parent.start)
                centerHorizontallyTo(parent)  // 擺放在 ConstraintLayout 水平中間
            })
 
            EditorTextField(secondEditor,"123456", Modifier.constrainAs(secondText) {
                top.linkTo(firstText.bottom, margin = 16.dp)
                start.linkTo(firstText.start)
                centerHorizontallyTo(parent)  // 擺放在 ConstraintLayout 水平中間
            })
 
            Button(
                onClick = {
                    onLoginBtnClick("123456", "123456")
                },
                // constrainAs() 將 Composable 組件與初始化的引用關(guān)聯(lián)起來
                // 關(guān)聯(lián)之后就可以在其他組件中使用并添加約束條件了
                modifier = Modifier.constrainAs(button) {
                    // 熟悉 ConstraintLayout 約束寫法的一眼就懂
                    // parent 引用可以直接用,跟 View 體系一樣
                    top.linkTo(secondText.bottom, margin = 20.dp)
                    start.linkTo(secondText.start, margin = 10.dp)
                }
            ){
                Text("Login")
            }
 
            Text(if (thumbUpSuccessful) "點(diǎn)贊成功" else "點(diǎn)贊失敗", Modifier.constrainAs(text) {
                top.linkTo(button.bottom, margin = 36.dp)
                start.linkTo(button.start)
                centerHorizontallyTo(parent)  // 擺放在 ConstraintLayout 水平中間
            })
 
 
        }
    }

關(guān)鍵代碼段就在于下面:

MVI+Compose架構(gòu)代碼

Text(if (thumbUpSuccessful) "點(diǎn)贊成功" else "點(diǎn)贊失敗", Modifier.constrainAs(text) {
   top.linkTo(button.bottom, margin = 36.dp)
   start.linkTo(button.start)
   centerHorizontallyTo(parent)  // 擺放在 ConstraintLayout 水平中間
})

TextView的text在頁面初始化的時候就跟數(shù)據(jù)源中的thumbUpSuccessful變量進(jìn)行了綁定,并且這個TextView不可以在其他地方二次賦值,只能通過這個變量thumbUpSuccessful進(jìn)行修改數(shù)值。當(dāng)然,使用這個方法,也解決了數(shù)據(jù)更新是無法diff更新的問題,堪稱完美了。

5.3MVI+Compose的優(yōu)缺點(diǎn)

MVI+Compose的優(yōu)點(diǎn)如下:

保證了框架的唯一性

由于每個view是在一開始的時候就被數(shù)據(jù)源賦值的,無法被多處調(diào)用隨意修改,所以保證了框架不會被隨意打亂。更好的保證了代碼的低耦合等特點(diǎn)。

MVI+Compose的也存在一些缺點(diǎn):

不能稱為缺點(diǎn)的缺點(diǎn)吧。

由于Compose實(shí)現(xiàn)界面,是純靠kotlin代碼實(shí)現(xiàn),沒有借助xml布局,這樣的話,一開始學(xué)習(xí)的時候,學(xué)習(xí)成本要高些。并且性能還未知,最好不要用在一級頁面。

六、如何選擇框架模式

6.1 架構(gòu)選擇的原理

通過上面這么多架構(gòu)的對比,可以總結(jié)出下面的結(jié)論。

耦合度高是現(xiàn)象,關(guān)注點(diǎn)分離是手段,易維護(hù)性和易測試性是結(jié)果,模式是可復(fù)用的經(jīng)驗(yàn)。

再來總結(jié)一下上面幾個框架適用的場景:

6.2框架的選擇原理

如果你的頁面相對來說比較簡單些,比如就是一個網(wǎng)絡(luò)請求,然后刷新列表,使用MVC就夠了。

如果你有很多頁面共用相同的邏輯,比如多個頁面都有網(wǎng)絡(luò)請求加載中、網(wǎng)絡(luò)請求、網(wǎng)絡(luò)請求加載完成、網(wǎng)絡(luò)請求加載失敗這種,使用MVP、MVVM、MVI,把接口封裝好更好些。

如果你需要在多處監(jiān)聽數(shù)據(jù)源的變化,這時候需要使用LiveData或者Flow,也就是MVVM、MVI的架構(gòu)好些。

如果你的操作是串行的,比如登錄之后進(jìn)行賬號驗(yàn)證、賬號驗(yàn)證完再進(jìn)行點(diǎn)贊,這時候使用MVI更好些。當(dāng)然,MVI+Compose可以保證你的架構(gòu)不易被修改。

切勿混合使用架構(gòu)模式,分析透徹頁面結(jié)構(gòu)之后,選擇一種架構(gòu)即可,不然會導(dǎo)致頁面越來越復(fù)雜,無法維護(hù)

上面就是對所有框架模式的總結(jié),大家根據(jù)實(shí)際情況進(jìn)行選擇。建議還是直接上手最新的MVI+Compose,雖然多了些學(xué)習(xí)成本,但是畢竟Compose的思想還是很值得借鑒的。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19178

    瀏覽量

    229200
  • 耦合器
    +關(guān)注

    關(guān)注

    8

    文章

    718

    瀏覽量

    59640
  • Android系統(tǒng)
    +關(guān)注

    關(guān)注

    0

    文章

    56

    瀏覽量

    13477
  • SpringMVC
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    5753
  • mvp模式
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    1185

原文標(biāo)題:Android架構(gòu)模式如何選擇

文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    協(xié)調(diào)器在啟動后,到底使用了哪一個信道呢?

    /mac_radio_defs.h中定義了:#define MAC_RADIO_CHANNEL_DEFAULT11/* 11信道 */請問:、這2定義是否有沖突?二、它們各有什么作用?三、協(xié)調(diào)器在啟動后,到底使用了
    發(fā)表于 08-06 11:10

    標(biāo)準(zhǔn)藍(lán)牙接口與專有射頻協(xié)議到底哪一個更好?

    專有射頻的優(yōu)點(diǎn)是什么?又有哪些缺點(diǎn)?標(biāo)準(zhǔn)藍(lán)牙接口與專有射頻協(xié)議到底哪一個更好?怎樣進(jìn)行選擇?
    發(fā)表于 06-26 06:42

    Arduino與單片機(jī)哪一個更重要

    Arduino是什么?為什么需要Arduino?Arduino與單片機(jī)哪一個更重要?
    發(fā)表于 09-02 06:48

    正確的AR8031的驅(qū)動程序是哪一個

    在設(shè)備源碼里找到了AT803x.c和enetc_pf.c文件,哪一個才是正確的AR8031的驅(qū)動程序?如果是enetc_pf.c的話,為什么ar8031需要使用pcie的驅(qū)動注冊函數(shù)?
    發(fā)表于 12-31 07:14

    Android中的alpha動畫是這個AlphaAnimation, 鴻蒙的alpha動畫是哪一個

    Android中的alpha動畫是這個AlphaAnimation,鴻蒙的alpha動畫是哪一個
    發(fā)表于 06-15 10:29

    軟件架構(gòu)設(shè)計(jì)之常用架構(gòu)模式

    分層架構(gòu):分層架構(gòu)是使用最多的架構(gòu)模式,通過分層使各個層的職責(zé)更加明確,通過定義的接口使各層之間通訊,上層使用下層提供的服務(wù)。分層分為:嚴(yán)格意義上的分層,般意義的
    發(fā)表于 06-22 18:35 ?4436次閱讀

    vivoX20和加5到底該買哪一個?

    vivoX20和加5到底該買哪一個?在這兩款手機(jī)之中的選擇主要還是看題主你側(cè)重哪一個方面了,這兩款手機(jī)的特點(diǎn)和賣點(diǎn)都不樣的,所以我們直接
    發(fā)表于 10-16 15:33 ?1.1w次閱讀

    10種常見的軟件體系架構(gòu)模式分析以及它們的用法、優(yōu)缺點(diǎn)

    架構(gòu)模式通用的、可重用的解決方案,用于在給定上下文中的軟件體系結(jié)構(gòu)中經(jīng)常出現(xiàn)的問題。架構(gòu)模式與軟件設(shè)計(jì)模式類似,但具有更廣泛的范圍。
    的頭像 發(fā)表于 01-31 12:39 ?2.2w次閱讀
    10種常見的軟件體系<b class='flag-5'>架構(gòu)模式</b>分析以及它們的用法、優(yōu)缺點(diǎn)

    詳解SOA五種基本架構(gòu)模式

    本文詳細(xì)解說了SOA五種基本架構(gòu)模式,面向服務(wù)的架構(gòu)(SOA)已成為連接復(fù)雜服務(wù)系統(tǒng)的主要解決方案。雖然SOA的理論很容易理解,但要部署設(shè)計(jì)良好、真正實(shí)用的SOA系統(tǒng)卻非常困難。本
    的頭像 發(fā)表于 02-07 14:41 ?2.1w次閱讀
    詳解SOA五種基本<b class='flag-5'>架構(gòu)模式</b>

    零線和火線應(yīng)該先接哪一個

    零線火線我們到底應(yīng)該先接哪一個?按照專業(yè)的電工操作來講,接線我們應(yīng)該先接零線,然后再來接火線;但是斷線的話是相反的,斷線先斷開火線,在斷開零線。如果我們再接插座的時候,如果家中沒有任何電器在
    的頭像 發(fā)表于 01-21 16:14 ?1.3w次閱讀

    關(guān)于邏輯和物理架構(gòu)模型開發(fā)之間的迭代

    方法,架構(gòu)活動都需要在邏輯架構(gòu)模型開發(fā)和物理架構(gòu)模型開發(fā)之間花費(fèi)幾次迭代,直到邏輯和物理架構(gòu)模
    的頭像 發(fā)表于 01-11 11:20 ?1843次閱讀

    邏輯架構(gòu)模型開發(fā)的目的和過程方法

    1.目的 邏輯架構(gòu)模型開發(fā)的目的是定義、選擇、和集成系統(tǒng)的邏輯架構(gòu)模型提供了框架,來驗(yàn)證
    的頭像 發(fā)表于 01-11 11:52 ?3611次閱讀

    嵌入式7種架構(gòu)模式分析

    ? 嵌入式軟件因?yàn)橛布Y源限制,可能存在驅(qū)動與應(yīng)用耦合的情況,但對于大型項(xiàng)目,資源充裕的情況下,復(fù)雜的業(yè)務(wù)邏輯、后續(xù)擴(kuò)展維護(hù)的需要,必須采用分層和模塊化思維,這種思想就是架構(gòu)模式般分7種
    的頭像 發(fā)表于 06-13 15:31 ?4409次閱讀
    嵌入式7種<b class='flag-5'>架構(gòu)模式</b>分析

    架構(gòu)模式的基礎(chǔ)知識

    ????作為軟件工程師,為什么至少要學(xué)習(xí)基本的架構(gòu)模式? ????我相信有很多人回答了這個問題,但我會給你些考慮的理由。 ????首先,如果您了解架構(gòu)模式的基礎(chǔ)知識,那么您就更容易遵循架構(gòu)
    的頭像 發(fā)表于 06-13 16:13 ?710次閱讀
    <b class='flag-5'>架構(gòu)模式</b>的基礎(chǔ)知識

    嵌入式軟件最常見的架構(gòu)模式

    嵌入式軟件因?yàn)橛布Y源限制,可能存在驅(qū)動與應(yīng)用耦合的情況,但對于大型項(xiàng)目,資源充裕的情況下,復(fù)雜的業(yè)務(wù)邏輯、后續(xù)擴(kuò)展維護(hù)的需要,必須采用分層和模塊化思維,這種思想就是架構(gòu)模式般分7種架構(gòu)模
    的頭像 發(fā)表于 06-22 10:32 ?2503次閱讀
    嵌入式軟件最常見的<b class='flag-5'>架構(gòu)模式</b>