让我们彻底看清MVC、MVP

时间:2023-03-08 15:54:51

这里開始记录下来自己对MVC、MVP、MVVM这三种框架模式的理解,本文从以下几个方面来梳理。

  • 架构的目的
  • 框架模式、设计模式
  • MVC设计的介绍
  • MVC在Android中的应用
  • MVC该怎样设计
  • MVP设计的介绍
  • MVP在Android中的应用

1. 架构的目的

当我们在进行OOP编程时,一切对象来源于需求,对象结合业务逻辑通过多态、继承、等封装成各个业务模块。我们通过设计将程序模块化,使模块内部高内聚和模块之间低耦合。

这样做的优点是,当我们进行开发或者測试时。我们仅仅须要专注于一点。而不用考虑牵一发动全身之类的问题,从而大大提高了我们的工作效率。可是设计是服务于目的的,就是说我们的需求决定着设计,不同的场景有些不同的需求,那么其应用的设计模式必定是不尽同样的。

万不可为了设计而设计,导致忘记了设计的初衷。

我们举个样例。假使我们的程序仅仅须要实现一个极其简单的功能以至于仅仅有寥寥数行代码,那这时候去考虑引入设计模式是不切实际的。但相反,对于系列庞大、功能众多的app我们就必须考虑引入一套符合情景的模式以梳理开发、简化维护。


2. 框架、设计模式

框架和设计模式是全然的两个概念。框架一般是代码的重用。而设计模式是设计的重用。架构则介于两者之间。部分代码重用,部分设计重用。

可能以上说法比較晦涩难懂。在此摘录一些从业者对它们的理解。

  • 框架一般是代码重用,模式是设计重用。
  • 设计模式是思考的过程,框架是思考的结果;
  • 框架须要用到设计模式。比方我们封装了一套retrofit+okhttp+rxjava网络请求框架,我们可能用到很多设计模式。比方:代理模式、观察者模式等。
  • 脱离实际需求谈框架都是耍流氓。
  • 框架是面包、面条、饺子…,设计模式是做面包面条饺子的手艺(至于怎么做、做成什么形状、味道…主要看手艺)。
  • 设计模式是大量场景总结出来的模版,假设你有对应的场景就能够往上套。

我们今天谈到的MVX系列是框架而不是设计模式。由于MVX框架是用来解决实际需求的(比方View、Model怎样分离相互控制)。


3. MVC设计的介绍

让我们彻底看清MVC、MVP

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzM2MjQ0ODY3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

MVC的全称是Model View Controller,如图是模型(model)-视图(view)-控制器(controller)的缩写。一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,在改进和个性化定制界面及用户交互的同一时候。不须要又一次编写业务逻辑。

当中M层处理数据。业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层。


4. MVC在Android中的应用

让我们彻底看清MVC、MVP

我们在文章的最開始说了框架的作用就是为了解决代码的臃肿,提高代码的复用率。在MVC模式中我们能够发现View、Model层都是遵循此原则设计的。

对于View层来讲。我们通常在做一个项目时封装出很多自己定义控件或者动画。封装较好的情况下在别的项目中我们能够直接拿来用,这是View层代码复用的体现。

对于Model层呢?我们能够讲Model理解为用来存储业务数据,当我们的同一app须要跨平台时(比方腾讯视频的apk版和腾讯云极光TV版)我们考虑是不是Model能够共用?

对于Controller层来讲,差点儿不可复用。由于它和业务逻辑联系太紧密。以下我们就来谈谈MVC在android中的体现。

上图为MVC模式在android项目中的体现,View代表视图层。Controller代表控制器。Model代表数据模型,app通过View视图监听控件依据控件表现调用对应的控制器处理业务逻辑,业务逻辑处理完之后控制器通知Model层改变,Model层改变带动View视图变化。

在android中一般採用xml文件进行界面描写叙述,这些xml文件能够视为app的View层,引用很easy,而且通过良好构造也能达到一定的复用性。model对应着各种体现业务逻辑的数据类。它们体现了app的数据(对数据库、网络操作)。而联通它们之间的桥梁就是Controller即Activity、Fragment,我们通过在activity/fragment来管理控制整个业务流程。

这里举个样例来形象地阐述下MVC在Android中的应用,比方一个饭店的做饭流程,做饭就要考虑做什么?拿什么做?这当中是怎样分工的。饭店老板戚总就好比我们的Controller,饭店的厨师老范相当于View(买什么菜他说了算是需求的来源)。採购小王相当于Model。厨师老范须要做个霸王别姬,须要一大堆食材。他把需求报告给戚总,戚总整理成清单要求小王去採购,小王採购完毕后直接将食材交给厨师老范。

这就是MVC的一个循环,当然饭店比較小,有的时候厨师老范仅仅须要一根葱这时候不是必需专门让採购跑一趟了吧也省点油钱。戚总顺手就买了。时间久了,戚总慢慢干了点採购的活…

我们举个简单的样例。

依据学生号码查询其名字、性别、年龄。

V层在xml文件里描写叙述例如以下:

让我们彻底看清MVC、MVP

M层数据类例如以下:

让我们彻底看清MVC、MVP

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzM2MjQ0ODY3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

C层例如以下:

让我们彻底看清MVC、MVP

到这里我们理解了MVC在android中的应用原理,那么我们进一步思考一下。假设app的业务逻辑相对简单,这么负责调控app的activity/fragment中的代码也相对简单。但当处理业务逻辑很繁琐的app时,activity/fragment中的代码就会变得相当臃肿不雅。很不利于维护。而且在实际开发过程中。我们常常在activity/fragment中写下这种代码,例如以下

textView.setText(string);

这样便违反了MVC的原则,在C中处理V,使得C的职责单一性被破坏。所以这时候MVP模型便走进了我们的视野。

下节我们先不讲MVP。当我们知道了MVC的现状后,我们该怎样合理地应用MVC模型。


5. MVC该怎样设计

MVC 尽管仅仅有三层,可是它并没有限制你仅仅能有三层。所以,我们能够将 Controller 里面过于臃肿的逻辑抽取出来,形成新的可复用模块或架构层次。

  • 将网络请求抽象到单独的类中;
  • 提供自己定义控件封装到专门的类中;
  • 构造 ViewModel;
  • 专门构造存储类。

通过代码的抽取,我们能够将原本的 MVC 设计模式中的 ViewController 进一步拆分,构造出 网络请求层、ViewModel 层、Service 层、Storage 层等其他类,来配合 Controller 工作,从而使 Controller 更加简单,我们的 App 更easy维护。

另外,不知道大家注意到没,事实上 Controller 层是很难于測试的。假设我们能够将 Controller 瘦身,就能够更方便地写 Unit Test 来測试各种与界面的无关的逻辑。

移动端自己主动化測试框架都不太成熟,可是将 Controller 的代码抽取出来。是有助于我们做測试工作的。


6. MVP设计的介绍

还记得我们上面的饭店吧。时间久了戚总有时候为了多省点油钱越来越多的担任採购的活了,甚至还兼职抄起了菜…慢慢的随着饭店的生意越来越好慢慢做大。戚总的事情越来越多開始忙只是来了。还有一方面饭店的生意好了却不见得多挣了多少钱。戚总想着会不会是厨师老范和採购小王串通坑他呢,于是乎。戚总想了一夜,哎嗨,一套新的管理机制来了。

View还是厨师老范,Model是採购小王,戚总把自己定位为Presenter,以后厨师老范有需求了仍然告诉戚总,戚总安排给小王去採购,採购完之后把食材交给戚总检查,数目账单没问题了戚总再将食材给厨师老范。这样不让厨师老范和採购小王就没有不论什么机会沟通避免他们串通。同一时候戚总以后仅仅负责吩咐两人做事也不为了省点油钱自己亲自干了毕竟生意做大了得像个老板的样子了,由此MVP模型初见雏形。

其示意图例如以下:

让我们彻底看清MVC、MVP

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzM2MjQ0ODY3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

这样做它有何利弊呢?我们仍然结合饭店的样例加以说明。

首先使它的优点:

  • 分工明白,大家各司其职就好提高效率;
  • 利于以后的发展。比方要裁人换人等,像MVC模式我假设换掉厨师,就会同一时候影响老板和採购的工作。MVP呢不管怎样调整我们仅仅调整P即可,其他模块互不干涉影响。
  • 大大解放老板的工作(是P层支付者调度降低代码臃肿)。
  • 用程序语言就是更加高内聚、低耦合;

再来谈谈它的弊端:

  • 结构复杂了。

整体而言。对于业务逻辑复杂的项目,MVP的利要远大于弊。


7. MVP在Android中的应用

以下我们就来谈谈MVP在Android之中的应用,既然是在Android中的应用,那么我们就考虑从大哥那儿学一套本事%>_<%。

这是Google大哥的官方demo。

再来一篇分析demo的文章。

研究完了上面的,我们是时候来点实践了。就模拟个登录功能吧。

首先看清文件结构:

让我们彻底看清MVC、MVP

google应用了BaseView、BasePresenter两个类。 例如以下:

让我们彻底看清MVC、MVP

让我们彻底看清MVC、MVP

这两个类作为全部view和presenter的基类来使用。

BasePresenter中的start方法是用来load页面时载入对应数据的。而登录模块临时并不须要该方法,可是这个类毕竟是为整个业务模块服务的。别的业务可能须要。临时保留。

BaseView中的setPresenter方法是为了向fragment中传递activity中new出来的presenter对象。登录模块事实上一个activity足以搞定,这种方法多余。可是保留该方法。理由同上。

開始详细业务。这里须要构建view和presenter。值得注意的点是。google将view和presenter放到了一个契约类中了。

所以

让我们彻底看清MVC、MVP

View中提供了四个相关操作,即登录成功、登录失败、获取username、获取密码。

Presenter中仅仅提供了login()登录操作。

让我们彻底看清MVC、MVP

这里activity作为mvp中的view层实现了契约类中的view接口。

public class LoginActivity extends Activity implements LoginContract.View{

    private Context mContext;
private EditText mNameEt, mPasswordEt;
private Button mLoginBtn;
private LoginContract.Presenter mPresenter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity);
mContext = this;
mPresenter = new LoginPresenter(this);
mNameEt = (EditText) findViewById(R.id.nameEt);
mPasswordEt = (EditText) findViewById(R.id.passwordEt);
mLoginBtn = (Button) findViewById(R.id.loginBtn);
mLoginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.login();
}
});
} @Override
public void loginSuccess() {
Toast.makeText(mContext, "登录成功", Toast.LENGTH_SHORT).show();
} @Override
public void loginFailed() {
Toast.makeText(mContext, "登录失败", Toast.LENGTH_SHORT).show();
} @Override
public String getLoginName() {
return mNameEt.getText().toString();
} @Override
public String getLoginPassword() {
return mPasswordEt.getText().toString();
} @Override
public void setPresenter(Object presenter) { } }

注意,假设你是fragment作为view,一定要加上mView.setPresenter(this)把P传递过去。

public class LoginPresenter implements LoginContract.Presenter{

    private LoginContract.View mView;

    public LoginPresenter(LoginContract.View view){
mView = view;
} @Override
public void login() {
boolean successFlag = httpLogin(mView.getLoginName(), mView.getLoginPassword());
if ( successFlag )
mView.loginSuccess();
else
mView.loginFailed();
} @Override
public void start() { }
}

Google通过一个契约类使我们的项目结构更加清晰。

当然没有绝对的MVP、MVC框架。比方在上面的登录功能中我们利用MVC更加方便简单。可是假设考虑以后的扩展性(比方添加第三方登录等等)利用MVP更加合适。

所以不管选择哪种方式必定是建立在需求的基础上的。


好了,临时告一段落吧…