一篇文章学会MVP

本文先介绍了MVC架构,包括其简介、在Android中的应用及示例,指出其在Android中存在Controller和View无法彻底分离、代码臃肿的缺点。接着引入MVP架构,阐述其从MVC演变而来,介绍了MVP的基本思想,并给出使用MVP实现天气查询功能的示例。

一、MVC

MVP是由MVC演变过来的,所以在学习MVP之前我们先看看MVC。

MVC介绍部分转载自天码营-《Android App的设计架构:MVC,MVP,MVVM与架构经验谈》。地址为https://www.tianmaying.com/tutorial/AndroidMVC

1、MVC简介

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

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

2、Android中的MVC

Android中界面部分也采用了当前比较流行的MVC框架,在Android中:

1)、视图层(View)

一般采用XML文件进行界面的描述,这些XML可以理解为AndroidApp的View。使用的时候可以非常方便的引入。同时便于后期界面的修改。逻辑中与界面对应的id不变化则代码不用修改,大大增强了代码的可维护性。

2)、控制层(Controller)

Android的控制层的重任通常落在了众多的Activity的肩上。这句话也就暗含了不要在Activity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Actiivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。

3)、模型层(Model)

我们针对业务模型,建立的数据结构和相关的类,就可以理解为AndroidApp的Model,Model是与View无关,而与业务相关的。对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。

3、MVC例子

1)、Controller控制器&View

代码如下:

public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener {

    private WeatherModel weatherModel;
    private EditText cityNOInput;
    private TextView city;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weatherModel = new WeatherModelImpl();
        initView();
    }

    //初始化View
    private void initView() {
        cityNOInput = findView(R.id.et_city_no);
        city = findView(R.id.tv_city);
        ...
        findView(R.id.btn_go).setOnClickListener(this);
    }

    //显示结果
    public void displayResult(Weather weather) {
        WeatherInfo weatherInfo = weather.getWeatherinfo();
        city.setText(weatherInfo.getCity());
        ...
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_go:
                weatherModel.getWeather(cityNOInput.getText().toString().trim(), this);
                break;
        }
    }

    // 传给Model的OnWeatherListener中的方法
    @Override
    public void onSuccess(Weather weather) {
        displayResult(weather);
    }

    // 传给Model的OnWeatherListener中的方法
    @Override
    public void onError() {
        Toast.makeText(this, 获取天气信息失败, Toast.LENGTH_SHORT).show();
    }

    private T findView(int id) {
        return (T) findViewById(id);
    }
}

从上面代码可以看到,Activity持有了WeatherModel模型的对象,当用户有点击Button交互的时候,Activity作为Controller控制层读取View视图层EditTextView的数据,然后向Model模型发起数据请求,也就是调用WeatherModel对象的方法 getWeather()方法。当Model模型处理数据结束后,通过接口OnWeatherListener通知View视图层数据处理完毕,View视图层该更新界面UI了。然后View视图层调用displayResult()方法更新UI,这时Activity又充当View层。至此,整个MVC框架流程就在Activity中体现出来了。上面流程中Activity充当了Controller和View,这两层没有实现解耦。

2)、Model

代码如下:

public interface WeatherModel {
    void getWeather(String cityNumber, OnWeatherListener listener);
}

................

public class WeatherModelImpl implements WeatherModel {
    /*这部分代码范例有问题,网络访问不应该在Model中,应该把网络访问换成从数据库读取*/
    @Override
    public void getWeather(String cityNumber, final OnWeatherListener listener) {

        /*数据层操作*/
        VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html,
                Weather.class, new Response.Listener<weather>() {
                    @Override
                    public void onResponse(Weather weather) {
                        if (weather != null) {
                            listener.onSuccess(weather);
                        } else {
                            listener.onError();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        listener.onError();
                    }
                });
    }
}

以上代码看出,这里设计了一个WeatherModel模型接口,然后实现了接口WeatherModelImpl类。controller控制器activity调用WeatherModelImpl类中的方法发起网络请求,然后通过实现OnWeatherListener接口来获得网络请求的结果通知View视图层更新UI 。至此,Activity就将View视图显示和Model模型数据处理隔离开了。activity担当contronller完成了model和view之间的协调作用。

至于这里为什么要定义接口WeatherModel?你考虑下这种情况:现在代码中的网络请求是使用Volley框架来实现的,如果哪天老板非要你使用Afinal框架实现网络请求,你怎么解决问题?难道是修改 getWeather()方法的实现? 这样修改不仅破坏了以前的代码,而且还不利于维护, 考虑到以后代码的扩展和维护性,我们选择设计接口的方式来解决着一个问题,我们实现另外一个WeatherModelWithAfinalImpl类,继承自WeatherModel,重写里面的方法,这样不仅保留了以前的WeatherModelImpl类请求网络方式,还增加了WeatherModelWithAfinalImpl类的请求方式。Activity调用代码无需要任何修改。

二、MVC的缺点

controller和view(除开xml中定义的view)都在Activity中实现,controller和view在android中无法做到彻底分离。View 和 Controller 层的耦合,导致 Activity 或者 Fragment 很臃肿,代码量很大。

三、MVP

大家都在Activity里面处理逻辑因为太方便了。 但是这样之后,我们Activity的职责太多了,耦合也严重,所以我们就想着怎么能给Activity减负,同时把耦合也降下来,所以就想找个哥们来替Activity分担的责任,大家最后都只有一个责任,各层关系也相对好理解。

既然Activty这么愿意和View搞到一起,那么就让他们俩在一起,共同负责View,我们在招聘一个职业经理人(Presenter)来处理事务,有啥事都找Presenter,你看着多方便啊,MVP是一个真正意义上的隔离View的细节和复杂性的模式。

1、简介

MVP 全称:Model-View-Presenter ;MVP 是从经典的模式 MVC 演变而来,它们的基本思想有相通的地方:Controller/Presenter 负责逻辑的处理,Model 提供数据,View 负责显示。

当 View 需要更新数据时,首先去找 Presenter,然后 Presenter 去找 Model 请求数据,Model 获取到数据之后通知 Presenter,Presenter 再通知 View 更新数据,这样 Model 和 View 就不会直接交互了,所有的交互都由 Presenter 进行,Presenter 充当了桥梁的角色。很显然,Presenter 必须同时持有 View 和 Model 的对象的引用,才能在它们之间进行通信。

2、mvp例子

下面例子使用MVP实现天气查询的功能。效果图如下:

1)、网络相关

网络请求采用Retrofit+RxJava实现,具体实现这里不作演示。我们先定义后台返回数据的实体类,已省略getter、

setter方法。代码如下:

public class WeatherInfo {
    private String reason;
    private ResultBean result;
    private int error_code;

    public static class ResultBean {
        private String city;
        private RealtimeBean realtime;
        private List<FutureBean> future;

        public static class RealtimeBean {
            private String temperature;
            private String humidity;
            private String info;
            private String wid;
            private String direct;
            private String power;
            private String aqi;

        }

        public static class FutureBean {
            private String date;
            private String temperature;
            private String weather;
            private WidBean wid;
            private String direct;


            public static class WidBean {
                private String day;
                private String night;

            }
        }
    }
}

2)、定义Model

这里提供一个GetRemoteListener,当网络请求完成时通过GetRemoteListener回调给调用者。

public interface GetRemoteListener<T> {
    void onSuccess(T t);
    void onError(String msg);
}

public class WeatherModel {
    public void getWeatherInfo(String city, final GetRemoteListener<WeatherInfo> listener) {
        HttpHelper.getWeatherInfoApi(city).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<WeatherInfo>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(WeatherInfo value) {
                        listener.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        listener.onError(e.getMessage());
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }
}

3)、定义接口

View的接口定义一个创建Presenter的方法,因为View中持有Presenter,所以View中都要创建Presenter,代码如下:

public interface BaseView {
    void createPresenter();
}

Presenter的接口中定义一个start方法,表示Presenter开始时处理的内容,代码如下:

public interface BasePresenter {
    void start();
}

4)、定义contract

每个View和Presenter都有一个接口,定义需要实现的功能。使用contract将View和Presenter的接口定义在一个类中,这样可以减少类和接口的总数。代码如下:

public class WeatherContract {
    public interface Presenter extends BasePresenter{
        void getWeatherInfo(String city);
    }

    public interface View extends BaseView{
        /**
         * 显示正在加载
         * @param isShow
         */
        void showLoading(boolean isShow);

        /**
         * 显示查询的天气信息
         * @param info
         */
        void showWeatherInfo(String info);

        /**
         * 清除显示的天气信息
         */
        void clearWeatherInfo();

        /**
         * 显示错误提示信息
         * @param info
         */
        void showErrInfo(String info);
    }
}

5)、实现View

View有两部分,一部分是xml中定义的,一部分是activity中定义的。xml代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    tools:context=".view.WeatherActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/et_city"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="城市"
            android:inputType="textPersonName" />

        <Button
            android:id="@+id/btn_support_city"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="支持城市查询"/>
    </LinearLayout>


    <Button
        android:id="@+id/btn_request"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="获取天气" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:background="@android:color/holo_orange_dark"
        android:gravity="center_horizontal"
        android:padding="20dp"
        android:visibility="visible" />

    <LinearLayout
        android:id="@+id/ll_err"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:orientation="vertical"
        android:padding="20dp"
        android:visibility="gone">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/error" />

        <TextView
            android:id="@+id/tv_err_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:gravity="center_horizontal" />
    </LinearLayout>

</LinearLayout>

Activity实现了View接口并且实现点击事件的处理,代码如下:

public class WeatherActivity extends AppCompatActivity implements WeatherContract.View, View.OnClickListener {
    private MaterialDialog dialog;
    private TextView tvResult;
    private LinearLayout llErrInfo;
    private TextView tvErrInfo;
    private EditText etCity;
    private WeatherContract.Presenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_weather);
        initView();
        setListener();
        createPresenter();
    }

    private void initView() {
        initDialog();
        tvResult = findViewById(R.id.tv_result);
        llErrInfo = findViewById(R.id.ll_err);
        tvErrInfo = findViewById(R.id.tv_err_info);
        etCity = findViewById(R.id.et_city);
    }

    private void setListener() {
        findViewById(R.id.btn_request).setOnClickListener(this);
        findViewById(R.id.btn_support_city).setOnClickListener(this);
    }

    private void initDialog() {
        MaterialDialog.Builder builder = new MaterialDialog.Builder(this);
        dialog = builder.content("拼命加载中...")
                .progress(true, 0)
                .build();
    }

    @Override
    public void createPresenter() {
        presenter = new WeatherPresenter(this);
    }

    @Override
    public void showLoading(boolean isShow) {
        if (isShow) {
            dialog.show();
        } else {
            dialog.dismiss();
        }
    }

    @Override
    public void showWeatherInfo(String info) {
        showWeatherOrErrInfo(true);
        tvResult.setText(info);
    }

    @Override
    public void clearWeatherInfo() {
        showWeatherOrErrInfo(true);
        tvResult.setText("");
    }

    @Override
    public void showErrInfo(String info) {
        showWeatherOrErrInfo(false);
        tvErrInfo.setText(info);
    }

    private void showWeatherOrErrInfo(boolean isShowWeather) {
        if (isShowWeather) {
            tvResult.setVisibility(View.VISIBLE);
            llErrInfo.setVisibility(View.GONE);
        } else {
            tvResult.setVisibility(View.GONE);
            llErrInfo.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_request:
                presenter.getWeatherInfo(etCity.getText().toString());
                break;
            case R.id.btn_support_city:
                startActivity(new Intent(this, SupportActivity.class));
                break;
        }
    }
}

6)、Presenter

Preaenter处于中间层,所以Presenter应该拥有View和Model。Presenter实现接口中的方法,实现逻辑处理功能,代码如下:

public class WeatherPresenter implements WeatherContract.Presenter {
    private WeatherModel model;
    private WeatherContract.View view;

    public WeatherPresenter(WeatherContract.View view) {
        this.view = view;
        model = new WeatherModel();
    }

    @Override
    public void getWeatherInfo(String city) {
        if(TextUtils.isEmpty(city)){
            view.showErrInfo("城市名不能为空");
            return;
        }

        view.showLoading(true);
        view.clearWeatherInfo();

        model.getWeatherInfo(city, new GetRemoteListener<WeatherInfo>() {
            @Override
            public void onSuccess(WeatherInfo weatherInfo) {
                view.showLoading(false);
                if(weatherInfo.getError_code() == 0) {
                    String format = ((Context) view).getString(R.string.weather_show_format);
                    WeatherInfo.ResultBean.RealtimeBean realtime = weatherInfo.getResult().getRealtime();
                    String showReslut = String.format(format, realtime.getTemperature(), realtime.getHumidity(), realtime.getAqi(),
                            realtime.getDirect(), realtime.getPower());
                    view.showWeatherInfo(showReslut);
                }else{
                    view.showErrInfo(weatherInfo.getReason());
                }
            }

            @Override
            public void onError(String msg) {
                view.showLoading(false);
                view.showErrInfo(msg);
            }
        });
    }

    @Override
    public void start() {

    }
}

 

标题基于SpringBoot的马术俱乐部管理系统设计与实现AI更换标题第1章引言介绍马术俱乐部管理系统的研究背景、意义、国内外研究现状、论文方法及创新点。1.1研究背景与意义阐述马术俱乐部管理系统对提升俱乐部管理效率的重要性。1.2国内外研究现状分析国内外马术俱乐部管理系统的发展现状及存在的问题。1.3研究方法以及创新点概述本文采用的研究方法,包括SpringBoot框架的应用,以及系统的创新点。第2章相关理论总结和评述与马术俱乐部管理系统相关的现有理论。2.1SpringBoot框架理论介绍SpringBoot框架的基本原理、特点及其在Web开发中的应用。2.2数据库设计理论阐述数据库设计的基本原则、方法以及在管理系统中的应用。2.3马术俱乐部管理理论概述马术俱乐部管理的基本理论,包括会员管理、课程安排等。第3章系统设计详细描述马术俱乐部管理系统的设计方案,包括架构设计、功能模块设计等。3.1系统架构设计给出系统的整体架构,包括前端、后端和数据库的交互方式。3.2功能模块设计详细介绍系统的各个功能模块,如会员管理、课程管理、预约管理等。3.3数据库设计阐述数据库的设计方案,包括表结构、字段设计以及数据关系。第4章系统实现介绍马术俱乐部管理系统的实现过程,包括开发环境、编码实现等。4.1开发环境搭建介绍系统开发所需的环境,包括操作系统、开发工具等。4.2编码实现详细介绍系统各个功能模块的编码实现过程。4.3系统测试与调试阐述系统的测试方法、测试用例以及调试过程。第5章系统应用与分析呈现马术俱乐部管理系统的应用效果,并进行性能分析。5.1系统应用情况介绍系统在马术俱乐部中的实际应用情况。5.2系统性能分析从响应时间、并发处理能力等方面对系统性能进行分析。5.3用户反馈与改进收集用户反馈,提出系统改进建议。第6章结论与展望总结马术俱乐部管理系统的设计与实现成果,并展望未来的研究
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值