从零开始学MVP架构

来源:互联网 发布:python基础教程 在线 编辑:程序博客网 时间:2024/06/05 07:36

前言

首先如果你是一个半年以内的安卓初学者,如果你对逻辑思维的感受不够强烈的话,我不建议你立马接触MVP设计模式。为什么呢?因为我自己就是这波人里面的…尴尬个三秒钟,跳过。这种东西具体是什么呢?

身边其实有很多类似的例子,如果非要举个例子,我个人感觉跟盖房子有那么一丢丢类似,但又不完全类似。

以一个大型公司为例,它会分很多的部门,然后每个部门具有一定的职能,但其实很多公司的部门架构都是具有一定的相似性的,这也就是我们通常说的模式化~而每个人做的事情也许别人也能做,就好比Activity里面你也可以做联网操作,也可以做View操作,但是你啥事儿都让他一个做是不是有点累呢?所以,合理的结构与分工可以让整个综合体更有效率的运行与维护。

在此之前,我希望初学者不是抱着一口吃个胖子的心态去接触这种设计模式。由更浅到浅,会让你理解更明白。在学习MVP以前,我强烈建议先学习MVC【标准MVC不是那种不三不四的结构的Demo或者渣渣项目】,把这种分模块与分层的思想带到你的项目中。这里是我的MVC学习笔记:http://www.jianshu.com/p/894420ac8fdc

不懂的哥们姐们多写几遍,一定不要懒~

入门

首先对于网上的那些理论性的玩意儿,我不想复制粘贴过来。一个原因,太抽象不具体。我们很难理解那些看不到的玩意儿。

如果你学会了MVC,那你应该就会有一种体会,就是你的联网操作,数据操作,以及一些耗时的操作等都是写在Model中的,然后你把数据塞到要显示的View中的时候,都是在Activity或Fragment等等这些Java文件中的,那么这么做的好处是啥?没有好处我们肯定不会用它的对吧,我想很多人刚开始学习安卓的时候,写demo,例如写一个显示天气数据的Activity,肯定你View也写在里头,联网也写在里头,然后第一步怎么联网,第二步回调怎么写数据,第三部怎么显示消失动画等等…其实这也没啥,但是你啥都写在一个里面,你一个Activity要写两千多行咩?协同开发的时候是不是给别人一种坑的感觉咩?

同样是写个显示天气数据的demo,使用MVC,你的View相关的操作,全部是在Activity中的,而你的联网等全部是在Model中的,这样的话,我们就吧视图【你眼睛看到的,手摸到的】与联网【比较耗时间的需要异步的】,这两种行为用代码隔开了。对于单个文件来说,降低代码量,易于维护。对于模块式的开发形式来说,独立的东西往往比黏合在一起的东西好控制,也就是我们常说的解耦。

然后,在我学会MVC以后我再写MVP就很容易明白它的目的了。首先MVC我们已经清楚,M是做耗时操作与业务逻辑处理的,View是我们眼睛看到的,你就把它当作布局文件就好不要想太复杂。然后C就是controller,就是我们把布局文件连接起来的,那不就是我们的Activity嘛。然后,MVP则相当于进化版本的MVC。我们知道,如果你要把一行文字塞到TextView中,要使用的是setText的方法,这个方法是针对TextView这个View的操作,而你塞进去,这一动作,我们要把它独立出来看,比如我吃饭,我的吃,与饭进到嘴巴里面,综合来看是一个行为动作,但是这里呢就把它进一步独立开,吃是逻辑,而饭进到嘴巴里才是针对你的嘴巴的操作。

以上逻辑的解释如果你还看不懂,哥哥我就不好说啥了…

代码【demo参照:http://blog.csdn.net/knxw0001/article/details/39637273/ 进行了少量修改】

我们要实现一个这样的功能:可以从EditText读取用户信息并存取,也可以根据ID来从后台读出用户信息并显示在EditText中。

首先来看一下分包

分包

看一下布局:

布局文件

布局文件的相关代码我就不贴上来了比较简单

然后看上面那个分包的图,首先如果你会MVC,你就知道Model的作用是放置耗时的操作与业务逻辑的模块,在这个demo中,Model要做的事情是什么?主要做的工作是,把从View中拿到的数据存起来,以及从存入的地方读出来并且拿出来使用。

UserBean

public class UserBean {    public UserBean(String first_name, String last_name) {        this.first_name = first_name;        this.last_name = last_name;    }    private String first_name;    private String last_name;    public String getFirst_name() {        return first_name;    }    public void setFirst_name(String first_name) {        this.first_name = first_name;    }    public String getLast_name() {        return last_name;    }    public void setLast_name(String last_name) {        this.last_name = last_name;    }}

看一下UserModel接口以及它的实现类UserModelImp,如果你不知道为啥子要写个实现类,建议你先去把MVC学会并且使用熟练。

UserModel

相关备注:UserModel主要做的工作是,把从View中拿到的数据存起来,以及从存入的地方读出来给别的地方用public interface UserModel {    void setId(int id);    void setFirstName(String first_name);    void setLastName(String last_name);    UserBean getUserInfo(int id);//通过id获取first_name与last_name,返回一个UserBean}

UserModelImp

相关备注:实现UserModel接口,来把具体要做的工作放在这里public class UserModelImp implements UserModel {    private int id;    private String first_name;    @Override    public void setId(int id) {        this.id = id;    }    @Override    public void setFirstName(String first_name) {        if (first_name != null && first_name.length() > 0) {            this.first_name = first_name;        } else {            Toast.makeText(MainApplication.MainContext, "首次姓名不能为空", Toast.LENGTH_SHORT).show();        }    }    @Override    public void setLastName(String last_name) {        if (last_name != null && last_name.length() > 0) {            MainApplication.userArray.append(id, new UserBean(first_name, last_name));            Toast.makeText(MainApplication.MainContext, "存入成功", Toast.LENGTH_SHORT).show();        } else {            Toast.makeText(MainApplication.MainContext, "最后姓名不能为空", Toast.LENGTH_SHORT).show();        }    }    @Override    public UserBean getUserInfo(int id) {        return MainApplication.userArray.get(id);    }}

那么在这个例子中的MVP的M部分已经完成,然后要看一下P,P是prensneter,意思是表现,展现出来,它很类似于controller,还以吃饭为例。如果controller表示吃饭这个整体动作,那么presenter就是拆分出来的吃,而View就是把饭送到嘴巴里的动作。

然后我们要先把饭送到嘴巴里才能吃对不对?看一下UserView接口。

UserView

相关备注:UserView可以做的工作是,从EditText塞入或读取数据,即与view的交互PS:对于View而言,它主要是面对View所做的操作,在本例中,对View的操作主要是从从EditText塞入或读取数据,然后与Button之间存在一定的交互public interface UserView {    int getId();    String getFirstName();    String getLastName();    void setFirstName(String first_name);    void setLastName(String last_name);    void clearText();}

然后是吃的逻辑,也就是presenter

UserPresenter

相关备注:Presenter主要起到了一个连接器的作用,也就是承载了大量原来在Activity中的操作PS:以本例子的需求为例,主要要做两方面的操作,一方面是存,一方面是取,那么要在这里把View和Model的作用发挥出来public class UserPresenter {    private UserView userView;    private UserModel userModel;    /*这里需要把View的实现类传入*/    public UserPresenter(UserView userView) {        this.userView = userView;        this.userModel = new UserModelImp();//这里是实现类    }    /*存的操作*/    public void saveUserInfo(int id, String first_name, String last_name) {        if (id != 0) {            userModel.setId(id);            userModel.setFirstName(first_name);            userModel.setLastName(last_name);        } else {            Toast.makeText(MainApplication.MainContext, "id不能为0或为空", Toast.LENGTH_SHORT).show();        }        userView.clearText();    }    /*取的操作*/    public void readUserInfo(int id) {        try {            if (id != 0) {                userView.setFirstName(userModel.getUserInfo(id).getFirst_name());                userView.setLastName(userModel.getUserInfo(id).getLast_name());            } else {                throw new Exception();            }        } catch (Exception e) {            e.printStackTrace();            Toast.makeText(MainApplication.MainContext, "未查询到此数据", Toast.LENGTH_SHORT).show();        }    }}

以上关于view和presenter的部署已经完成,接下来就要在Activity中来使用了。

MainActivity

public class MainActivity extends AppCompatActivity implements UserView, View.OnClickListener {    private EditText etUserID;    private EditText etUserFirstName;    private EditText etUserLastName;    private Button btnSave;    private Button btnRead;    private UserPresenter userPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_home);        initView();        userPresenter = new UserPresenter(this);    }    private void initView() {        etUserID = (EditText) findViewById(R.id.etUserID);        etUserFirstName = (EditText) findViewById(R.id.etUserFirstName);        etUserLastName = (EditText) findViewById(R.id.etUserLastName);        btnSave = (Button) findViewById(R.id.btnSave);        btnRead = (Button) findViewById(R.id.btnRead);        btnSave.setOnClickListener(this);        btnRead.setOnClickListener(this);    }    @Override//此方法里没有完全隔离【后期修改】    public int getId() {        if (etUserID.getText().toString().length() > 0) {            return Integer.parseInt(etUserID.getText().toString());        } else {            return 0;        }    }    @Override    public String getFirstName() {        return etUserFirstName.getText().toString();    }    @Override    public String getLastName() {        return etUserLastName.getText().toString();    }    @Override    public void setFirstName(String first_name) {        etUserFirstName.setText(first_name);    }    @Override    public void setLastName(String last_name) {        etUserLastName.setText(last_name);    }    @Override    public void clearText() {        etUserID.setText("");        etUserFirstName.setText("");        etUserLastName.setText("");    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btnSave:                userPresenter.saveUserInfo(getId(), getFirstName(), getLastName());                break;            case R.id.btnRead:                userPresenter.readUserInfo(getId());                break;        }    }}

程序入口:MainApplication

public class MainApplication extends Application {    public static SparseArray<UserBean> userArray = new SparseArray<>();    public static Context MainContext;    @Override    public void onCreate() {        super.onCreate();        MainContext = this;    }}

可以看到,Activity里已经看不到Model的影子了,Model和View之间的交互全部放在了Presenter里面,很好的隔离了耗时操作了联网等与Activity黏性过高的问题,因为这个例子很经典,也很简单,可以比作一个简单化的登陆demo,但是这个例子还有不足的地方。有时候一个Activity里我们可能会做更多复杂的操作与逻辑,这样的情况下我们可能会写多个Presenter与Model,合理分包,合理的放置逻辑,都是为了实现模块化,独立化的开发思想。

演示效果

3.gif

其他

对于开发来说,有几个比较重要的个人过程。一个是基本理论,理论你都不懂怎么能让程序正常的跑起来?第二个就是思维,思维影响效率,比如有人做家务,他会先做饭,做完饭再扫地,扫完地再洗衣服,可能有的人就是饭正在烧的时候,他打开了洗衣机洗衣服,然后同时去扫地。这就是思维方式的差异,我希望通过最浅显易懂的方式,来把复杂的问题解释清楚,当然如果实在无法理解,也许就是个人天生的思维逻辑不够完善。