挑战MVP

作者: 程序员丶星霖 | 来源:发表于2017-06-29 10:38 被阅读181次

    挑战MVP

    MVP.jpg

    前言

    之前一直听说MVP,听到这种模式有多么好,但是一直没有尝试过这种模式,也没有花时间去理解它。但是听周围的朋友都在项目中使用到了这种模式,便萌生了尝试一下的念头。笨鸟,还飞得晚,活该自己落后与别人哦。来吧,下了决心,怎么也得理解一下是个什么一回事吧。总不能还停留在“最有价值球员”的认知上吧!!!

    一、什么是MVP?

    MVP?今年不是杜兰特吗?这个我知道啊!

    逗逼.jpg

    这里的MVP可不再是NBA了哦,我还是醒醒吧!天天就知道不务正业。书归正传,说到了MVP,跟我们联系比较紧密的也就是MVC了,这俩哥们儿是啥关系啊,姓氏都一样,是不是哥俩儿啊?

    仔细一看,还这有那么几分神似。

    MVC,全称Model—View—Controller(模型-视图-控制器),这种模式是在80年代Small talk-80出现多的一种设计模式,后来被得到了广泛的应用。

    MVP,全称Model—View—Presenter(模型-视图-表示器),这种模式是由IBM开发出来的一个针对C++和Java的编程模型,大概出现于2000年,普遍认为这种模式是MVC模式的一个变种。

    二、MVC与MVP的对比

    代码结构 MVC MVP
    Model 业务逻辑和实体模型 业务逻辑和实体模型
    View 对应于布局文件 对应于Activity,负责View的绘制以及与用户交互
    Controller 对应于Activity ——
    Presenter —— 负责完成View与Model之间的交互

    从上面的表格对比中可以看得出来,MVP大大的减少了Activity中的职责,简化其中代码量,将View与Model之间的交互的复杂逻辑代码都提取到了Presenter中进行处理。这样做的好处就是降低了耦合性,模块职责划分更明显,便于进行测试。将原本复杂的Model-View关系转变为更加清晰明了的Model-View关系。

    • 未使用MVP模式的情况下:
    未使用MVP.png
    • 使用MVP模式的情况下:
    MVP.png

    下图中是MVC与MVP的图示:

    MVC与MVP.png

    两种模式最大的区别是:

    • MVC中允许Model与View进行交互,也就导致了我们时常看到上千行代码的MainActivity;而MVP中将Model与View之间的交互交给Presenter完成,并且Presenter和View是通过接口完成交互的。

    三、MVP的优缺点对比

    MVP对比 优点 缺点
    1 降低耦合度,实现Model与View的完全分离 Presenter中的逻辑处理较多,是得Presenter比较臃肿,维护比较困难
    2 模块职责划分明显。层次清晰 View的渲染放到了Presenter中,导致View与Presenter的交互过于频繁
    3 隐藏了数据,安全性提高 View与Presenter联系紧密,一旦View发生改变,Presenter也将发生改变
    4 Presenter的复用性提高 代码量增加,代码复杂度增加
    5 便于进行测试
    6 代码灵活性增加
    7 View可以进行组件化

    四、MVP剖析

    M(model)层主要职责:

    • 从网络、数据库、文件、传感器、第三方等数据源进行读写数据;
    • 对外部的数据类型进行解析转换为APP内部数据交由上层处理;
    • 对数据的临时存储、管理、协调上层数据请求。

    V(view)层主要职责:(Activity、Fragment、View、ViewGroup等)

    • 提供UI交互;
    • 在Presenter的控制下修改UI;
    • 将业务事件交由Presenter进行处理。

    P(presenter)层主要职责:

    • 作为View与Model交互的中间纽带,处理与用户交互的逻辑;
    • 根据用户在View中的行为去更新Model;
    • 从View中获取数据然后发送给Model。

    MVP中通常包含4个要素:

    • View:负责绘制UI元素,与用户进行交互;
    • View interface:需要View来实现的接口,View通过接口与Presenter进行交互,降低耦合,方便进行单元测试;
    • Model:负责存储、检索、操纵数据;
    • Presenter:作为View与Model交互的中间枢纽,处理与用户交互的逻辑。

    五、一次练个够

    到底是个啥?还是上代码,才能更好的理解啊!是骡子是马拉出来溜溜啊~~~~

    5.1先来实现一个简单的登录功能吧!(向鸿洋大神学习下!http://blog.csdn.net/lmj623565791/article/details/46596109
    UI图.png
    1. 要登录总要有用户吧,来个bean!
    /**
     * 用户类
     */
    
    public class User {
        private String userName;
        private String passWord;
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String mUserName) {
            userName = mUserName;
        }
    
        public String getPassWord() {
            return passWord;
        }
    
        public void setPassWord(String mPassWord) {
            passWord = mPassWord;
        }
    }
    
    1. 有了用户,他需要做什么操作呢?实现登录功能,至少有个登录吧。
    public interface IUser {
        public void login(String userName,String passWord,OnLoginListener mOnLoginListener );
    }
    
    public class UserModel implements IUser {
        @Override
        public void login(final String userName, final String passWord, final OnLoginListener mOnLoginListener) {
            //模拟子线程耗时操作
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException mE) {
                        mE.printStackTrace();
                    }
                    //模拟登录成功
                    if (userName.equals("闭关修炼") && passWord.equals("123")) {
                        User mUser = new User();
                        mUser.setUserName(userName);
                        mUser.setPassWord(passWord);
                        mOnLoginListener.loginSuccess(mUser);
                    } else {
                        mOnLoginListener.loginFailed();
                    }
                }
            }.start();
        }
    }
    
    public interface OnLoginListener {
        void loginSuccess(User mUser);
        void loginFailed();
    }
    
    1. Presenter与View的交互式通过接口实现的。所以难点就是确定View中应该有什么方法,对我这个初级菜鸟来说,简直就是难于上青天啊!

    登录肯定需要有用户名和密码,那么就需要在EditText中获取吧:

    //登录说明了要有用户名和密码
    String getUserName();
    String getPassWord();
    

    登录的过程是个耗时操作,需要给用户一个有好的提示,一般是操作ProgressBar等待框:

    void showLoading();
    void hideLoading();
    

    登录存在两种情况的处理,分别是成功后和失败后的逻辑处理:

    void toMainActivity(User mUser);
    void showFailedError();
    

    还有一个重置功能(当然写在一个方法中也是可以的):

    void clearUserName();
    void clearPassWord();
    

    所以View的接口应该是这样的:

    /**
     * View的接口
     */
    
    public interface ILoginView {
    
        //登录说明了要有用户名和密码
        String getUserName();
        String getPassWord();
    
        //耗时操作,需要给用户一个有好的提示,一般就是操作Progress Bar
        void showLoading();
        void hideLoading();
    
        //登录存在两种可能要处理的情况,登陆成功于登录失败
        void toMainActivity(User mUser);
        void showFailedError();
    
        //重置功能
        void clearUserName();
        void clearPassWord();
    
    }
    

    View Interface都有了,哪个View实现就可以了啊!

    public class LoginActivity extends Activity implements ILoginView {
    
        private TextInputEditText mEtUserName;
        private TextInputEditText mEtPassWord;
        private Button mBtnLogin;
        private Button mBtnReset;
        private ProgressBar mProgressBar;
    
        private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
            initView();
        }
    
        //初始化
        private void initView() {
            mEtUserName = (TextInputEditText) findViewById(R.id.et_username);
            mEtPassWord = (TextInputEditText) findViewById(R.id.et_password);
            mBtnLogin = (Button) findViewById(R.id.btn_login);
            mBtnReset = (Button) findViewById(R.id.btn_reset);
            mProgressBar = (ProgressBar) findViewById(R.id.progressbar);
            mBtnLogin.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mUserLoginPresenter.login();
                }
            });
            mBtnReset.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mUserLoginPresenter.clear();
                }
            });
        }
    
        @Override
        public String getUserName() {
            return mEtUserName.getText().toString();
        }
    
        @Override
        public String getPassWord() {
            return mEtPassWord.getText().toString();
        }
    
        @Override
        public void showLoading() {
            mProgressBar.setVisibility(View.VISIBLE);
        }
    
        @Override
        public void hideLoading() {
            mProgressBar.setVisibility(View.GONE);
        }
    
        @Override
        public void toMainActivity(User mUser) {
            Toast.makeText(this, "登录成功!!!", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void showFailedError() {
            Toast.makeText(this, "登录失败!!!", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void clearUserName() {
            mEtUserName.setText("");
        }
    
        @Override
        public void clearPassWord() {
            mEtPassWord.setText("");
        }
    }
    

    哎,等等,这是啥! private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);别激动,之前不就说过Presenter和View Interface进行交互嘛。现在View实现了Interface。那肯定 得有个桥梁起作用啊。

    这个桥梁主要处理的就是登陆和重置的操作,要完成两者的交互,肯定需要有两者的实现类。大概就是从View中获取所需参数,交由Model进行业务处理;执行结束后的反馈和结果,在给View进行相应的显示。

    /**
     * Presenter是用作Model和View之间交互的桥梁
     */
    
    public class UserLoginPresenter {
        private IUser mIUser;
        private ILoginView mILoginView;
        private Handler mHandler = new Handler();
    
        public UserLoginPresenter(ILoginView mILoginView) {
            this.mILoginView = mILoginView;
            mIUser= new UserModel();
        }
    
        public void login(){
            mILoginView.showLoading();
            mIUser.login(mILoginView.getUserName(), mILoginView.getPassWord(), new OnLoginListener() {
                @Override
                public void loginSuccess(final User mUser) {
                    //需要在UI线程执行
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mILoginView.toMainActivity(mUser);
                            mILoginView.hideLoading();
                        }
                    });
                }
    
                @Override
                public void loginFailed() {
                    //需要在UI线程执行
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mILoginView.showFailedError();
                            mILoginView.hideLoading();
                        }
                    });
                }
            });
        }
    
        public void clear(){
            mILoginView.clearUserName();
            mILoginView.clearPassWord();
        }
    }
    

    参考资料:

    欢迎大家关注我的微信公众号

    二维码.jpg

    相关文章

      网友评论

      本文标题:挑战MVP

      本文链接:https://www.haomeiwen.com/subject/ykkmcxtx.html