通過例子手撕架構模式

sydMobile發表於2019-12-04

通過例子手撕架構模式
更多精品文章分類

關於架構

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

一般開發軟體的時候,我們是如何進行架構設計的呢?

首先一個 APP 軟體是一個大的系統,我們通常可以把這個大的系統劃分為許多個小的模組,比如:登入註冊功能,首頁展示功能、個人資訊功能等等某個具體的模組的功能。然後我們就可把這幾個相對獨立的模組分別劃分給不同的人員進行開發。

當然在進行模組劃分前,我們需要先把整個軟體的架子搭建起來,比如:請求網路需要用什麼架構、載入圖片需要使用什麼架構、傳遞資料需要什麼架構等等,這裡的架構用作第三方庫替代一下可能更加貼切一下。但是這的確是我們整個 App 架構的一部分,可以說是最底層的一部分,我把這部分稱為底層技術架構。

然後在這一層的基礎上,衍生出相對公共的業務層。比如登入、註冊、一些通用的業務,這些模組相對來說比較獨立,變化性不大,在這一層上面就是核心的業務層了,變動比較大。

注意這些層次並不是物理上的層次。

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

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

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

為什麼會出現架構模式

架構模式的出現是為了讓程式碼更加的清晰,相互之間耦合性低,非常龐大的專案,便於以後的迭代升級,讓程式劃分更加清晰(檢視顯示、業務邏輯/資料處理都獨立開)這就是進行架構模式的意義,你想如果你的程式非常龐大結果你就全部都寫在了一個java 類中,一個 java 類中幾萬行程式碼,是不是想想都恐怖,當然任何普通人都不會這麼寫吧,都會有自己的架構模式,比如有的人把這個 java 類拆分了 4 個類,有的拆了 10 個類。等等這其中就有一種是比較適合的,隨著不斷的發展,就有人提出了 MVC 這種架構模式,使用這種架構模式,可以讓 java 類中的不同內容分離,比其他人的方式更加合理,於是就有了 MVC 架構模式

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

架構模式的目的

  • 模組化功能

    不同的功能使用不同的模組,不要全部大雜燴。合理的架構模式會讓程式碼各部分相互獨立,耦合性降低。

  • 提高開發效率

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

  • 便於日後維護

    便於擴充套件維護

不能為了設計而設計,要根據專案實際情況來,如果專案很小,弄上覆雜的架構模式,效果反而不好。

對於量級比較小的專案,只需要劃分一下層次,規範一下,提煉一下方法就可以了。

對於量級較大的專案,才需要引入較為複雜的框架。

下面分別來講 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.im/post/5d762c…

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

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

www.jianshu.com/p/f17f5d981…

相關文章