MVC和MVP MVVM MVI

本文介绍了MVC、MVP、MVVM和MVI四种信息技术架构。MVC存在View和Controller分工不明显等缺点;MVP将MVC的V和C结合,引入Presenter,实现M与V解耦,但V层需定义大量接口;MVVM通过双向绑定降低耦合,提高可维护性,但定位bug较难;MVI是严格数据单向且状态管理单一不可变。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MVC:

参考见:Android框架模式——MVC - 简书

M:model,模型(业务逻辑):进行数据库的读写等等

V:view,视图(控件,XML):进行数据的显示

C:controller(Activity等界面逻辑,接受V层输入同时传出给M层进行逻辑运算);

其中:button是view,方法是controller.某个属性是model :但是button也可以直接在逻辑中设置属性,所以这样就会和model混淆;

View可以直接和model进行数据之间的传递,也可以通过controller传给model;

缺点就是:View和Controller分工不明显,同时会让Controller的Activity越来越臃肿;

举例:

C+V层:

View层:XML布局文件activity_mvcpattern代表的就是View层,用来显示布局,与用户进行交互。

Controller层:MVCActivity代表的是Controller层,View层会传递请求至Controller,Controller控制Model层进行业务的更新。

 
/**
 * @author hongri
 * @date 2018/9/4
 *
 * Controller层
 */
public class MVCActivity extends AppCompatActivity implements View.OnClickListener {
 
    private Button btnRequest;
    private ImageView iv;
    private MVCHttpRequestModel mvcHttpRequestModel;
    private static final String requestUrl = "https://avatar.csdnimg.cn/5/7/C/1_u012440207.jpg";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /**
         * xml文件属于理论上的View层
         */
        setContentView(R.layout.activity_mvcpattern);
 
        mvcHttpRequestModel = new MVCHttpRequestModel();
 
        btnRequest = findViewById(R.id.btnRequest);
        iv = findViewById(R.id.iv);
        btnRequest.setOnClickListener(this);
 
    }
 
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnRequest:
 
                /**
                 * Controller层将网络请求业务逻辑交由Model层处理
                 */
                mvcHttpRequestModel.onHttpRequest(requestUrl, new MVCRequestCallback() {
 
                    @Override
                    public void onSuccess(Object successData) {
                        if (successData != null) {
                            iv.setImageBitmap((Bitmap)successData);
                        }
                    }
 
                    @Override
                    public void onFailure(Object failureData) {
                        Toast.makeText(MVCActivity.this, failureData.toString(), Toast.LENGTH_LONG).show();
                    }
                });
                break;
            default:
                break;
        }
    }
}

M层:

这里的MVCHttpRequestModel便属于Model层,Model层主要用于处理数据库、网络请求等一系列复杂的耗时处理,Model数据请求完成后,通知View进行UI的更新。

 
 
/**
 * @author hongri
 * @date 2018/9/4
 *
 * Model层
 */
 
public class MVCHttpRequestModel implements MVCHttpRequestInterface {
 
    @Override
    public void onHttpRequest(final String urlString, final MVCRequestCallback listener) {
 
        new RequestTask(listener).execute(urlString);
 
    }
 
    public class RequestTask extends AsyncTask<String, Void, Bitmap> {
 
        private MVCRequestCallback listener;
 
        public RequestTask(MVCRequestCallback listener) {
            this.listener = listener;
        }
 
        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap bitmap = null;
            InputStream inputStream;
            try {
                URL url = new URL(strings[0]);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(10 * 1000);
                int responseCode = conn.getResponseCode();
 
                if (responseCode == 200) {
                    inputStream = conn.getInputStream();
                    bitmap = BitmapFactory.decodeStream(inputStream);
                } else {
                    bitmap = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bitmap;
        }
 
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
 
            /**
             *Model层请求完数据后回调给Controller层,Controller层更新UI
             */
            if (bitmap != null) {
                listener.onSuccess(bitmap);
            } else {
                listener.onFailure("获取失败");
            }
        }
    }
}

因此MVP将MVC中的V和C结合生成MVP中的V,引入新概念Presenter

MVP:

M层:业务层,具体逻辑功能实现

V层:视图层(包含了Activity,不同于MVC的V),用作显示

P层:连接层,用于连接M和V层桥梁

P层持有M层引用,为了调用M层中逻辑去计算结果;同时P层持有V引用,为了拿到M层逻辑后去显示在界面上;

V层持有P层引用,为了向P层中传用户参数或者启动P层中的调用逻辑;

优点:1.模型M与视图层V完全解耦,修改不会互相影响

2.可将一个Presenter用于多个视图,就算视图层修改大量逻辑,Presenter层不需要大量改动;

缺点:V层需要定义大量接口和P层进行交互,因此用到的Activity要实现所有的V中的接口方法,比较复杂;

下面举个简单的连网络例子:

看下所谓的MVP:M,V,P层都由接口来进行和其他层的数据传递;

Model层:

按理应该Bean类也算在Model层,由于此例只用String做Bean类,因此这里省去Bean类的构造

BaseNet.java:
定义一个请求服务器的方法

package app.project.mvp.model;

/**
* Created by QLY on 2016/9/1.
*/
public interface BaseNet {
    String getDataFromServer();
}

BaseNetIml.java:
实现类

package app.project.mvp.model;

import android.util.Log;

/**
* Created by QLY on 2016/9/1.
*/
public class BaseNetIml implements BaseNet{

    @Override
    public String getDataFromServer() {
        String data = "this is data from server"
        return data;
    }
}

Presenter层:

BasePresenter .java:

package app.project.mvp.presenter;

/**
* Created by QLY on 2016/9/1.
* 页面业务逻辑(一般都要参与服务器交互)
*/
public interface BasePresenter {
    void loadData();
    void loadResult();
}

PresenterIml.java

负责拿到BaseView 、BaseNet的引用,从而实现交互

package app.project.mvp.presenter;

import android.util.Log;

import app.project.mvp.model.BaseNet;
import app.project.mvp.model.BaseNetIml;
import app.project.mvp.view.BaseView;

/**
* Created by QLY on 2016/9/1.
*/
public class PresenterIml implements BasePresenter {
    private BaseView mvpView;
    private BaseNet baseNet;

    public PresenterIml(BaseView mvpView) {
        this.mvpView = mvpView;
        baseNet = new BaseNetIml();
    }


    @Override
    public void loadData() {
        mvpView.loading();
        baseNet.getDataFromServer();
    }

    @Override
    public void loadResult() {
        Log.i("test", "loadResult: 服务器返回结果。。。");
        mvpView.cancelLoading();
    }
}

View层:

 BaseView.java:
一共2个方法,注释很清晰,当然我们的view(activity)要实现这个接口

package app.project.mvp.view;

/**
* Created by QLY on 2016/8/31.
*
* UI逻辑接口,一般有activity、Fragment实现
*/
public interface BaseView {
    /**
    * 显示加载进度
    */
    void showLoadResult(String data);
    /**
    * 隐藏加载进度
    */
    void cancelLoading();

}

接下来就是activity了:

package app.project.mvp.view;


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.Random;

import app.project.R;
import app.project.mvp.presenter.BasePresenter;
import app.project.mvp.presenter.PresenterIml;

public class TestActivity extends AppCompatActivity implements BaseView {
    private PresenterIml mPresenter;
    boolean flag;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        mPresenter = new PresenterIml(this);
        //开始请求服务器
        mPresenter.loadData();
        //加载完成
        mPresenter.loadResult();
    }

    @Override
    public void showLoadResult(String data) {
        //...
        Toast.make(data);
    }

    @Override
    public void cancelLoading() {
            Log.i("test", "loading  加载完成。。。");
    }

}

可以看看这个:  http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html

优化:

因为P的实现类持有V层的引用,这样会造成内存泄漏;需要在P层新增destroy方法及时将view引用置空

eg:

public class PresenterIml implements BasePresenter {
    private BaseView mvpView;
    private BaseNet baseNet;

    public PresenterIml(BaseView mvpView) {
        this.mvpView = mvpView;
        baseNet = new BaseNetIml();
    }


    ...

    ...


    @Override
    public void onDestory() {
        mvpView == null;
    }

}

MVVM:

Model层: 负责数据存储、读取网络数据,操作数据库I/O等操作比如某个数据仓库啥的包含了获取数据逻辑;

ViewModel:只做和业务逻辑与数据操作相关,不做和UI有关的事;通过UI控件主动和它绑定,数据源Model发生改变,viewmodel将主动驱动View去做UI变化;

View:纯粹的视图层,包括Activity,Fragment和XML等,会设置viewmodel的监听器,同时Model层通过setValue方法更新值也发生在这里;

(引用是单向依赖的,但V和ViewModel是双向绑定的)

View 持有 ViewModel 的引用

ViewModel 持有 Model 的引用

Model 不持有其他层的引用

MVP和MVVM的区别:

P层虽然和ViewModel层相似,也是承担着V和M层之间交互的职责;但是P是持有V层对象,调用一些V的接口方法实现;而ViewModel是通过DataBinding方式和V层实现双向绑定;

举例说明(model少了个仓库类,模拟数据获取):

ViewModel:

View层(一般构建一个viewmodel对象并持有它进行数据操作):

上面View缺少了获取完数据源后主动设值的过程

下面补上:

{
...
UserRepository.getUserRepository().getUsersFromServer(new Callback<List<User>>() {
            @Override
            public void onSuccess(List<User> users) {
                loginVm.login(users.get(0).name,users.get(0).password);
            }
        });
...
}

Model层 (Bean类+仓库类):

纯粹读数据库,网络操作等获取数据的类:

public class UserRepository {

private static UserRepository mUserRepository;
public static UserRepository getUserRepository(){
    if (mUserRepository == null) {
        mUserRepository = new UserRepository();
    }
    return mUserRepository;
}

//(假装)从服务端获取
public void getUsersFromServer(Callback<List<User>> callback){
    new AsyncTask<Void, Void, List<User>>() {
        @Override
        protected void onPostExecute(List<User> users) {
            callback.onSuccess(users);
            //存本地数据库
            saveUsersToLocal(users);
        }
        @Override
        protected List<User> doInBackground(Void... voids) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //假装从服务端获取的
            List<User> users = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                User user = new User("user"+i, i);
                users.add(user);
            }
            return users;
        }
    }.execute();
}

优点:

1.提高可维护性。Data Binding可以实现双向的交互,使得视图和控制层之间的耦合程度进一步降低,分离更为彻底,同时减轻了Activity的压力。

2.简化测试。因为View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。

缺点:

出现bug难以定位,可能是View层问题也可能是Model数据源获取出问题;

MVI

MVVM的View和ViewModel是双向绑定的,ViewModel变化自动更新UI。

而MVI是严格数据单向的,

View → Intent → ViewModel → State → View

且状态管理是单一不可变的:

// 状态 不可变
sealed class UiState {
    object Loading : UiState()
    data class Success(val data: String) : UiState()
    data class Error(val message: String) : UiState()
}

// 意图 不可变
sealed class UiIntent {
    object LoadData : UiIntent()
    object Retry : UiIntent()
}

// ViewModel
class MyViewModel : ViewModel() {
    private val _state = MutableStateFlow<UiState>(UiState.Loading)
    val state: StateFlow<UiState> = _state

    fun processIntent(intent: UiIntent) {
        when (intent) {
            UiIntent.LoadData -> loadData()
            UiIntent.Retry -> loadData()
        }
    }

    private fun loadData() {
        viewModelScope.launch {
            _state.value = UiState.Loading
            try {
                val result = repository.fetchData()
                _state.value = UiState.Success(result)
            } catch (e: Exception) {
                _state.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

在Compose中状态提升:

@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    val state by viewModel.state.collectAsState()
    
    // 状态提升到顶层
    MyScreenContent(
        state = state,
        onIntent = viewModel::processIntent
    )
}

@Composable
fun MyScreenContent(
    state: UiState,
    onIntent: (UiIntent) -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        when (state) {
            is UiState.Loading -> {
                CircularProgressIndicator()
                // 初始加载
                LaunchedEffect(Unit) {
                    onIntent(UiIntent.LoadData)
                }
            }
            is UiState.Success -> {
                Text(text = state.data)
                Button(onClick = { onIntent(UiIntent.Retry) }) {
                    Text("Refresh")
                }
            }
            is UiState.Error -> {
                Text(text = state.message, color = Color.Red)
                Button(onClick = { onIntent(UiIntent.Retry) }) {
                    Text("Retry")
                }
            }
        }
    }
}

如果有多层级组件:

@Composable
fun ParentComponent() {
    val viewModel: MyViewModel = viewModel()
    val state by viewModel.state.collectAsState()
    
    ChildComponentA(
        state = state,
        onIntent = viewModel::processIntent
    )
}

@Composable
fun ChildComponentA(
    state: UiState,
    onIntent: (UiIntent) -> Unit
) {
    Column {
        when (state) {
            is UiState.Success -> {
                ChildComponentB(
                    data = state.data,
                    onRefresh = { onIntent(UiIntent.Retry) }
                )
            }
            // 其他状态处理...
        }
    }
}

@Composable
fun ChildComponentB(
    data: String,
    onRefresh: () -> Unit
) {
    Text(text = data)
    Button(onClick = onRefresh) {
        Text("Refresh Data")
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值