經過例子手撕架構模式

更多精品文章分類

關於架構

關於架構的概念很寬泛,不是一句 MVP、MVC、MVVM 就能說清楚的。java

通常開發軟件的時候,咱們是如何進行架構設計的呢?數據庫

首先一個 APP 軟件是一個大的系統,咱們一般能夠把這個大的系統劃分爲許多個小的模塊,好比:登陸註冊功能,首頁展現功能、我的信息功能等等某個具體的模塊的功能。而後咱們就可把這幾個相對獨立的模塊分別劃分給不一樣的人員進行開發。markdown

固然在進行模塊劃分前,咱們須要先把整個軟件的架子搭建起來,好比:請求網絡須要用什麼架構、加載圖片須要使用什麼架構、傳遞數據須要什麼架構等等,這裏的架構用做第三方庫替代一下可能更加貼切一下。可是這的確是咱們整個 App 架構的一部分,能夠說是最底層的一部分,我把這部分稱爲底層技術架構。網絡

而後在這一層的基礎上,衍生出相對公共的業務層。好比登陸、註冊、一些通用的業務,這些模塊相對來講比較獨立,變化性不大,在這一層上面就是核心的業務層了,變更比較大。架構

注意這些層次並非物理上的層次。mvc

至於 MVC、MVP、MVVM 也常被稱爲軟件架構,維基百科的定義就是:是軟件工程中的一種軟件架構模式。把軟件系統分爲了避免同的部分,好比 MVC 把軟件系統分爲了三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。框架

其實提到 MVC、MVP、MVVM 我我的更傾向於,這是針對軟件中的某個功能或者業務使用這種書寫方式,每一個模塊都是用了這種模式,那麼總體的軟件提及來就能夠說是這個 APP 是用了 MVC 模式。其實說白了 MVC、MVP、MVVM 等等都是一種寫代碼的設計規範(可能這個詞不太準確)沒有具體的概念,爲了之後更加方便的迭代代碼,使代碼看起來更加的清晰。更偏向於一種書寫代碼的模型,一種代碼理念,而不是新的技術。這種理念就是爲了讓代碼更清晰,耦合性下降,還要根據具體的業務來使用,不能照搬硬套。ide

下面再來具體說一下 MVC、MVP、MVVM 只是介紹一下用這三種模式來寫代碼,代碼的結構應該是什麼樣的,只是通用,並非說只能這樣,具體根據業務來 下面所說的軟件架構模式都是指的某種編寫代碼的理念,好比:MVP、MVC 甚至是你本身的理念。模塊化

爲何會出現架構模式

架構模式的出現是爲了讓代碼更加的清晰,相互之間耦合性低,很是龐大的項目,便於之後的迭代升級,讓程序劃分更加清晰(視圖顯示、業務邏輯/數據處理都獨立開)這就是進行架構模式的意義,你想若是你的程序很是龐大結果你就所有都寫在了一個java 類中,一個 java 類中幾萬行代碼,是否是想一想都恐怖,固然任何普通人都不會這麼寫吧,都會有本身的架構模式,好比有的人把這個 java 類拆分了 4 個類,有的拆了 10 個類。等等這其中就有一種是比較適合的,隨着不斷的發展,就有人提出了 MVC 這種架構模式,使用這種架構模式,可讓 java 類中的不一樣內容分離,比其餘人的方式更加合理,因而就有了 MVC 架構模式oop

上面的三種架構模式,都有本身的使用模型,使用不一樣的模式,代碼層次劃分的也不同。

架構模式的目的

  • 模塊化功能

    不一樣的功能使用不一樣的模塊,不要所有大雜燴。合理的架構模式會讓代碼各部分相互獨立,耦合性下降。

  • 提升開發效率

    開發人員只須要專於某一塊內容(視圖顯示、業務邏輯)

  • 便於往後維護

    便於擴展維護

不能爲了設計而設計,要根據項目實際狀況來,若是項目很小,弄上覆雜的架構模式,效果反而很差。

對於量級比較小的項目,只須要劃分一下層次,規範一下,提煉一下方法就能夠了。

對於量級較大的項目,才須要引入較爲複雜的框架。

下面分別來說 MVC MVP MVVM 在 Android 開發中的運用,只是針對 Android 項目開發。

經過項目分析

這裏有個登陸功能,功能很簡單。如圖:

就是一個模擬登陸的功能,輸入用戶名,密碼後點擊登陸進行頁面登陸,登陸結果會給頁面一個反饋。

沒有架構

咱們先來看看沒有架構的時候,是怎麼進行開發的

爲了便於說明我在代碼中有標識數字,便有後面解釋。

頁面內容很簡單,這裏就再也不粘貼 xml 頁面部分了,只粘貼 java 代碼部分。

public class LoginActivity extends BaseActivity {

    @BindView(R.id.tv_username)
    TextView tvUsername;
    @BindView(R.id.et_username)
    EditText etUsername;
    @BindView(R.id.et_password)
    EditText etPassword;
    @BindView(R.id.bt_login)
    Button btLogin;
    @BindView(R.id.bt_clear)
    Button btClear;
    @BindView(R.id.pb)
    ProgressBar pb;
    @BindView(R.id.tv_status)
    TextView tvStatus;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_login);
        ButterKnife.bind(this);
    }

    @Override
    public void initView() {
        super.initView();
        btClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // ① 
                etPassword.setText("");
                etUsername.setText("");

            }
        });
        // 這裏接受用戶點擊事件傳遞,再進行登陸前須要進行頁面邏輯判斷
        btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (etUsername.getText() == null || etUsername.getText().toString().trim().equals("")) {
                    Toast.makeText(LoginActivity.this, "用戶名不能爲 null", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (etPassword.getText() == null || etPassword.getText().toString().trim().equals("")) {
                    Toast.makeText(LoginActivity.this, "密碼不能爲 null", Toast.LENGTH_SHORT).show();
                    return;
                }
                login(etUsername.getText().toString(), etPassword.getText().toString());

            }
        });
    }

    /** * 登陸業務,牽扯到數據 * 真實狀況下,有多是 網絡請求、本地數據庫操做、大量數據邏輯操做 * @param name * @param password */
    public void login(String name, String password) {
        FormBody formBody = new FormBody.Builder()
                .add("username", name)
                .add("password", password)
                .build();
        Request request = new Request.Builder()
                .post(formBody)
                .url("http://192.168.1.120/login")
                .build();
        OkHttpUtil.enqueue(request, new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                // 處理一些業務邏輯
                tvStatus.setText("登陸失敗");
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                // 來更新頁面內容
                tvStatus.setText("登陸成功");
            }
        });
    }
}
複製代碼

這就是沒有使用架構進行開發的代碼,很明顯的不足就是全部的代碼都放在了 Activity 中,無論是對 view 的操做仍是對數據處理仍是一些頁面的邏輯判斷,這樣的代碼寫的多了,可讀性和可擴展性都會變的很是差。

不得不說,我上面寫的代碼這樣看上去仍是不夠亂,其實一方面是咱們的功能不夠複雜,功能很簡單,你纔可以一眼就看清楚,試想裏面若是有幾十個幾百個邏輯呢?(你可能會說了,我能夠把一些內容抽離出去,其實這就是一種簡單的架構思想,只不過可能不太符合 mvc 架構的核心思想,可是隻要是適合你的項目就是最好的架構),另外一方面是,實際上是 Android 開發自己是遵循必定的 MVC 思想的。View 放在 xml 中與 Java 代碼解耦,而後在 Activity 中充當 Controller 處理邏輯控制,可是這樣有一個問題就是沒有對 Model 進行劃分,並且 xml 功能太簡單隻能做爲一個靜態的頁面,不免會在 Activity 中操做頁面。這樣也就致使了 Activity 裏面的功能過多,既要充當 View 的功能又要充當 controller 的功能。

MVC

通常MVC

首先通常在 Android 中提到 MVC 的時候,都是這樣來分類的

  • View:對應佈局文件
  • Model:牽扯到數據的業務邏輯和實體模型
  • Controller:對應 Activity,進行操控 Model 和 頁面,還有部分頁面邏輯判斷

可是細看你就會發現,View 對應佈局文件,佈局文件就是一個 xml 能夠作的事情實在是太少了,通常就是寫完頁面就完事了,真正動態修改頁面的時候仍是須要經過在 Activity 中獲取對應的控件來進行修改。很顯然這樣的結果就是 Activity 既要作 View 的部分工做又要作 Controller 的部分工做,其實這種模式就是 MV 沒有了 C,是一種不標準的 MVC 模式。

按照這個思路寫代碼

// 定義了一個 View 的接口,把 View 要作的功能列出來
// 由於 Model 須要通知 View 刷新頁面,經過接口的方式能夠減小 Model 對某個頁面的依賴
public interface IView {
     void setLoginStatus(String loginStatus);

     void loginFail(String fail);
}

// 這就是咱們的 View 和 Controller 的集合體
public class LoginMVCActivity extends BaseActivity implements IView {
    @BindView(R.id.tv_username)
    TextView tvUsername;
    @BindView(R.id.et_username)
    EditText etUsername;
    @BindView(R.id.et_password)
    EditText etPassword;
    @BindView(R.id.bt_login)
    Button btLogin;
    @BindView(R.id.bt_clear)
    Button btClear;
    @BindView(R.id.pb)
    ProgressBar pb;
    @BindView(R.id.tv_status)
    TextView tvStatus;
    LoginModel loginModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_login);
        ButterKnife.bind(this);
    }

    @Override
    public void initView() {
        super.initView();
        loginModel = new LoginModel(this);
        btClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                etPassword.setText("");
                etUsername.setText("");

            }
        });
        btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 這樣的判斷邏輯是屬於 Controller 的職責的
                if (etUsername.getText() == null || etUsername.getText().toString().trim().equals("")) {
                    Toast.makeText(LoginMVCActivity.this, "用戶名不能爲 null", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 這樣的判斷邏輯是屬於 Controller 的職責的
                if (etPassword.getText() == null || etPassword.getText().toString().trim().equals("")) {
                    Toast.makeText(LoginMVCActivity.this, "密碼不能爲 null", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 調用 Model 中的登陸業務邏輯
                loginModel.login(etUsername.getText().toString(), etPassword.getText().toString());

            }
        });
    }

	
    @Override
    public void setLoginStatus(String loginStatus) {
        tvUsername.setText(loginStatus);
    }

    @Override
    public void loginFail(String fail) {
        tvUsername.setText(fail);
    }

}


// 有了 IView ,這樣 LoginModel 就不會依賴某個具體的 activity 了,想要複用也能夠了,
// 否則 LoginModel 就是專門爲某個 Activity 而準備的
public class LoginModel {
    private IView iView;
    public LoginModel(IView iView){
        this.iView = iView;
    }


    public void login(String name,String password){
        FormBody formBody = new FormBody.Builder()
                .add("username",name)
                .add("password",password)
                .build();
        Request request = new  Request.Builder()
                .post(formBody)
                .url("http://192.168.1.120/login")
                .build();
        OkHttpUtil.enqueue(request, new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                // 登陸失敗了,真實狀況下可能會通過一些邏輯判斷,而後通知頁面發生更新
                //..... 等等一些邏輯處理
                iView.loginFail("");
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                //..... 等等一些邏輯處理
                // 來更新頁面內容
                iView.setLoginStatus("登陸成功");
            }
        });
    }

}
複製代碼

這種寫法是把 Controller 和 View 混在一塊兒了,讓 M 分離了,實際這就模式是 MV,Activity 責任不明確、十分臃腫,除了擔任了 View 層的部分職責(加載應用佈局、接受用戶操做、更新佈局內容)還有擔任 Controller 層的職責(部分關於頁面的邏輯),這樣隨着界面內容增多,邏輯變得根據複雜的話,Activity 中的代碼會不斷增長。下面來進一步升級一下這個不太標準的 MVC。

升級版MVC

// 首先把 Activity 中應該 Controller 來作的內容抽離出來
// 使用接口的好處就是依賴沒有那麼強,能夠有不一樣的實現,相互不影響,再一個就是便於閱讀,有的時候咱們不須要關心具體是怎麼實現的,只要知道有哪些方法,這些方法能夠作什麼就能夠了,這樣在接口裏面一眼就能夠看到了,若是沒有接口的話就要看方法,方法就意味這有方法體,若是實現過程很複雜,一個類中有好幾十個這種方法,那麼看起來很費勁。
public interface ILoginController {

    void login(String name,String password);
}


public class LoginController implements ILoginController{
    private IView iView;
    private ILoginModel iLoginModel;

    public LoginController(IView iView,ILoginModel iLoginModel){
        this.iView = iView;
        this.iLoginModel = iLoginModel;
    }

    @Override
    public void login(String name, String password) {
        if (name== null || name.trim().equals("")) {
            iView.loginFail("用戶名不能爲 null");
            return;
        }
        if (password == null || password.trim().equals("")) {
            iView.loginFail("密碼不能爲 null");
            return;
        }
        iLoginModel.login(name, password);
    }
}

public interface IView {
     void setLoginStatus(String loginStatus);

     void loginFail(String fail);

     void error(String error);
}

public class LoginMVCActivity extends BaseActivity implements IView {
    @BindView(R.id.tv_username)
    TextView tvUsername;
    @BindView(R.id.et_username)
    EditText etUsername;
    @BindView(R.id.et_password)
    EditText etPassword;
    @BindView(R.id.bt_login)
    Button btLogin;
    @BindView(R.id.bt_clear)
    Button btClear;
    @BindView(R.id.pb)
    ProgressBar pb;
    @BindView(R.id.tv_status)
    TextView tvStatus;
    LoginModel loginModel;
    ILoginController loginController;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_login);
        ButterKnife.bind(this);
    }

    @Override
    public void initView() {
        super.initView();
        loginModel = new LoginModel(this);
        loginController = new LoginController(this, loginModel);
        btClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                etPassword.setText("");
                etUsername.setText("");

            }
        });
        btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginController.login(etUsername.getText().toString(),
                        etPassword.getText().toString());

            }
        });
    }

    @Override
    public void setLoginStatus(String loginStatus) {
        tvUsername.setText(loginStatus);
    }

    @Override
    public void loginFail(String fail) {
        tvUsername.setText(fail);
    }

    @Override
    public void error(String error) {
        Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
    }

}


public interface ILoginModel {
    void login(String name,String password);
}

public class LoginModel implements ILoginModel{
    // 一般狀況下 Model不會直接持有 View,而是經過接口回調(具體的接口實如今 Controller 中實現)來更新View
    private IView iView;
    public LoginModel(IView iView){
        this.iView = iView;
    }


    public void login(String name,String password){
        FormBody formBody = new FormBody.Builder()
                .add("username",name)
                .add("password",password)
                .build();
        Request request = new  Request.Builder()
                .post(formBody)
                .url("http://192.168.1.120/login")
                .build();
        OkHttpUtil.enqueue(request, new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                // 登陸失敗了,真實狀況下可能會通過一些邏輯判斷,而後通知頁面發生更新
                //..... 等等一些邏輯處理
                iView.loginFail("");
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                //..... 等等一些邏輯處理
                // 來更新頁面內容
                iView.setLoginStatus("登陸成功");
            }
        });
    }

}
複製代碼

好了,到此這個升級版本的 MVC 就完成了,這樣結構看上去就很清晰了,各層次之間互相分離。

如今的 MVC,M就是一個單獨的類,V 就是 Activity 和 xml,C 也用了一個單獨類

雖然咱們已經作了升級比以前的 MV 版本好了不少,可是你會發現 MVC 之間相互依賴的關係太多,V 能夠和 C 直接進行通訊,而 M 和 V 之間也能夠直接進行通訊,這就致使了 M 須要依賴 V,彼此之間依賴關係太多。因而就出現了 MVP,MVP的出現就是爲了完全斷絕 M 和 V 之間的聯繫。

總結:MVC

M:包括用於操做與數據有關的複雜邏輯和實體模型

V:專門用於頁面的展現

C:一些簡單的邏輯(不牽扯到數據),串聯 M 和 V

MVC最致命的問題就是 M 能夠和 V進行通訊

來自網絡

經過這張圖就能夠看出,View Controller Model 之間相互依賴關係過多。

MVP

爲了解決升級版 MVC存在的一些問題,出現了 MVP

  • M Model 數據層,用於操做與數據有關的複雜的業務邏輯和定義實體模型
  • V 視圖層,View 的繪製刷新,用戶交互 對應 Android 中的 Activity 和 xml
  • P Presenter 用於鏈接 V 層和 M層,處理一些簡單的業務邏輯

在 MVP 中 M和V之間是不能通訊的,必需要藉助 P

代碼實現:

// M 部分
public interface ILoginModel {
    void login(String name,String password);
}

public class LoginModel implements ILoginModel {
	// 這裏也能夠不持有 LoginPresenter 經過持有一個回調來完成
    ILoginPresenter iLoginPresenter;
    public LoginModel(ILoginPresenter iLoginPresenter){
        this.iLoginPresenter = iLoginPresenter;
    }
    public void login(String name,String password){
        FormBody formBody = new FormBody.Builder()
                .add("username",name)
                .add("password",password)
                .build();
        Request request = new  Request.Builder()
                .post(formBody)
                .url("http://192.168.1.120/login")
                .build();
        OkHttpUtil.enqueue(request, new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                // 登陸失敗了,真實狀況下可能會通過一些邏輯判斷,而後通知 Presenter
                //..... 等等一些邏輯處理
                iLoginPresenter.result("");
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                //..... 等等一些邏輯處理
                // 通知 Presenter
                iLoginPresenter.result("");
            }
        });
    }
}

// V 部分

public interface IView {
     void setLoginStatus(String loginStatus);

     void loginFail(String fail);

     void error(String error);
}

public class LoginMVPActivity extends BaseActivity implements IView {


    @BindView(R.id.tv_username)
    TextView tvUsername;
    @BindView(R.id.et_username)
    EditText etUsername;
    @BindView(R.id.et_password)
    EditText etPassword;
    @BindView(R.id.bt_login)
    Button btLogin;
    @BindView(R.id.bt_clear)
    Button btClear;
    @BindView(R.id.pb)
    ProgressBar pb;
    @BindView(R.id.tv_status)
    TextView tvStatus;
    // Presenter
    ILoginPresenter loginPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_login);
        ButterKnife.bind(this);
    }

    @Override
    public void initView() {
        super.initView();
        loginPresenter = new LoginPresenter(this);
        btClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                etPassword.setText("");
                etUsername.setText("");

            }
        });
        btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter.login(etUsername.getText().toString(),
                        etPassword.getText().toString());

            }
        });
    }

    @Override
    public void setLoginStatus(String loginStatus) {
        tvUsername.setText(loginStatus);
    }

    @Override
    public void loginFail(String fail) {
        tvUsername.setText(fail);
    }

    @Override
    public void error(String error) {
        Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
    }
}

// Presenter

public interface ILoginPresenter {
    void login(String name,String password);
    void result(String name);
}


public class LoginPresenter implements ILoginPresenter {

    IView iView;
    ILoginModel iLoginModel;

    public LoginPresenter(IView iView){
        this.iView = iView;
        // 不想讓 Model持有 Presenter 的時候,一樣這裏能夠傳入一個 CallBack的實現
        // 當 Model 完成後回調一下就能夠了
        iLoginModel = new LoginModel(this);
    }

    @Override
    public void login(String name, String password) {
        if (name== null || name.trim().equals("")) {
            iView.loginFail("用戶名不能爲 null");
            return;
        }
        if (password == null || password.trim().equals("")) {
            iView.loginFail("密碼不能爲 null");
            return;
        }
        iLoginModel.login(name, password);
    }

    @Override
    public void result(String name) {
        // 這裏就是能夠進行一些邏輯判斷,而後根據狀況通知頁面刷新
        if(name.equals("==")){
            iView.loginFail(name);
        }else {
            iView.setLoginStatus(name);
        }
    }
}

複製代碼

特色總結

這樣一個最簡單的 MVP 模式的代碼就完成了,對比升級版 MVC最大的特色就是阻斷了 M 和 V 之間的關係。P 持有 Model 和 View,Model 和View 的一切通訊都是經過 P 來完成。

這樣就演變成只有 P 依賴於 Model 和 View 了(都經過接口實現,不依賴具體的類),作到了彼此的分離。

來自網絡

M:Model 與數據有關的邏輯業務

View:View 的繪製,視圖展現,與用戶交互 Activity

P:Presenter 串聯 M 和 V,完成 M 和 V 層的交互,部分業務邏輯處理,持有 M 和 V 的對象。

MVP 的封裝

MVP 寫法是有必定規律的,而且層次分的很明顯,這裏咱們封裝一下 MVP 中共用的一些操做,做爲三個對應的底層接口

interface BaseModel{
    
}

interface BaseView{
    void showError(String msg);
}

public abstract class BasePresenter<V extends BaseView,M extends BaseModel>{
    protected V view;
    protected M model;
    
    public BasePresenter(){
        model = createModel();
    }
    
    void attachView(V view){
        this.view = view;
    }
    // 爲了防止內存泄漏 Activity 銷燬的時候將 View 置空
    void detachView(){
        this.view = null;
    }
    
    abstract M createModel();
}

// 這裏這三個就是各個層的基類,能夠根據需求在裏面寫一些通用的方法
// BasePresenter 利用了泛型,Presenter 必須同時持有 View 和 Model 的引用,可是在底層接口中沒法肯定他們的類型,
// 只能肯定他們是 BaseView 和 BaseModel 的子類,因此採用泛型的方式來引用,就解決這個問題了,在 BasePresenter 的子類中只要定義好 View 和 Model 的類型,就會自動應用他們的對象了。
複製代碼

而後爲了方便查看和修改把同一個頁面的內容所有放到一個 Contract 契約接口中

// 這樣查看起來就很是的清晰了
interface TestContract{
    interface Model extends BaseModel{
        void method1(Callback1 callback1);
        void method2(Callback2 callback2);
        .....根據項目所需的業務添加方法聲明;
    }
    interface View extends BaseView{
        void updateUI1();
        void updateUI2();
        ....根據實際其餘添加方法聲明;
    }
    abstract class Presenter extends BasePresenter<View,Model>{
        abstract void request1();
        abstract void request2();
       	.....根據項目添加適當的方法聲明;
    }
    
    interface Callback1{
        void handlerResult();
        ......根據實際狀況添加;
    }
    
}
複製代碼

而後在 Activity 或者 Fragment 中去實現 TestContract.View 的接口,再分別建立兩個類用來實現 Test.Contract.Presenter 和 Test.Contract.Model 方法就能夠了。

總結

全部的架構目的都是爲了讓代碼擴展性更強,彼此依賴性更低,各個層功能更獨立更專注。這樣就致使不斷的分層,理論上說分的層次越多,各個層次的功能就越專注,付出 的代價就是代碼結構會變得複雜。因此這個分幾層要根據項目來決定!

M 是負責業務邏輯和數據模型的組合,其實要說的話,M 下面應該還有層次(獲取數據)(進行具體的網絡查詢業務等等不過通常狀況就這就把這些內容寫入到 M 裏面了)

V 就是視圖層

X 應該是用來進行表現層業務邏輯的,表現層說簡單一點其實就是操控頁面應該如何顯示,與頁面有直接關係的一些邏輯。這些邏輯不叫作業務邏輯而叫作表現層邏輯。

參考:www.jianshu.com/p/f17f5d981…

mp.weixin.qq.com/s?__biz=MzI…

blog.csdn.net/lmj62356579…

juejin.cn/post/684490…

mp.weixin.qq.com/s/_6p6vfce7…

mp.weixin.qq.com/s/OEzcsPZHC…

www.jianshu.com/p/f17f5d981…

相關文章
相關標籤/搜索