小序
随着开发框架的潮流影响,再加上组织上给予的压力。看来不得不迈出MVC君的守望之门了,仿佛一股热流向此处涌来,其实际上已然有好多前辈写下了大写的文章在等着我。此刻起只要好好了解一番,再弄一两个小Demo便可造就一片新的天地了,然后就可以在蓝天白云下沐浴在阳光下奔跑了,光想想还是有点小激动的。
MVC
1.1.介绍
1.2.还是介绍
以上介绍主要是绘制并介绍了MVC层与层之间的交互过程,下面具体整理下各个层的含义与作用域:
who | what | where |
---|---|---|
Model | 业务逻辑处理层 | 数据源处理,网络加载,复杂算法 |
View | 界面显示层 | 布局文件.xml |
Controller | 控制器 | Activity、Fragment |
1.3.潜在输出
- 将内容显示以及业务逻辑处理分隔开来,模块职责划分明确,有利于代码维护;
- 提升项目的可扩展性与维护性,并降低耦合度;
- 对业务逻辑复杂且页面较多的项目更能提升MVC的优势;
1.4.承受伤害
- View与Controller融合在一起。大多数人通过Activity/Fragment直接控制View内容的更新;
- View完全依赖Model。忽略Controller的控制直接从Model获取数据;
- View自己承担部分业务逻辑。Model失去可重用的业务逻辑处理;
- Controller同时负责View与Model的任务处理。使得代码异常臃肿,显得较为混乱;
1.5.代码飘过
/**
8. Controller
*/
public class MainActivity extends AppCompatActivity {
// 文本
private TextView mMainTv;
// 按钮
private Button mMainBtn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
/**
* View
*/
private void initView() {
this.mMainTv = (TextView) findViewById(R.id.main_tv);
this.mMainBtn = (Button) findViewById(R.id.main_btn);
mMainBtn.setOnClickListener(v -> {
mMainTv.setText(getDateTimeStr());
});
}
/**
* Model
*
* @return 当前日期时间
*/
private String getDateTimeStr() {
Date curDate = new Date(System.currentTimeMillis());
return SimpleDateFormat.getDateTimeInstance().format(curDate);
}
}
以上代码只是简单实现按钮点击获取当前的时间,并赋值给文本显示。其主要是表现大多数停留在MVC的伙伴,都会以这样一个方式去创建模块的页面和功能。至少接触过的很多项目里是这样的,当然并没有反对的意思,因为只要合理分配也是极好的。这里更多的是为了鉴于这个类来比较:
MVP
2.1.介绍
2.2.依然是介绍
同理,以上介绍主要是绘制并介绍了MVP层与层之间的交互过程。其情节内容没有改动,因为一个功能的思想一样不受使用模式的影响。下面具体整理下各个层的含义与作用域:
who | what | where |
---|---|---|
Model | 数据处理层 | 数据的检索,存储等操作 |
View | 界面显示层 | Activity、Fragment、布局文件.xml |
Presenter | 层现器 | 作为View与Model两者的中间枢纽,处理与用户交互的负责逻辑 |
2.3.潜在输出
- View层与Model层完全分离,两者间的修改互不影响;
- Presenter控制View与Model之间的交互,大大提升模型的使用效率;
- Presenter可以被多个View绑定(MVC当中Controller服务多个View);
- 可完全脱离用户接口实现单元测试;
- 解决MVC当中Activity代码臃肿的问题;
2.4.承受伤害
- 由于Presenter可以被多个View绑定,且如果与Presenter之间的交互现象过于频繁,一旦View需要变更,那么Presenter也需要变更从而影响Presenter的复用性;
- 大量的View与Model的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难;
2.5.前期装备
啥?还有前期装备,这是要逆天嘛,还有这装备到底是什么。请恕我直言,如果前期没有辅助那必须吃力些呀,所以View有理由学会摆脱与Presenter的直接交互,继而通过View interface来配合View接受Presenter返回结果的处理。下面是前期装备属性:
- 降低View与Presenter之间耦合度;
- 方便进行单元测试,可绕过View直接定义类实现interface模拟View与Presenter之间的交互;
2.6.后期装备
啥?还有后期,这发育的也太狠了吧。请恕我直言,这装备我也还没用过。当然它的属性是很诱人的,满满的被动输出伤害有木有。其实际上就是前辈们挖掘出来的两种View模式,用于划分View与Presenter的任务内容,具体如下:
who | what |
---|---|
PV(Passive View) | View的UI元素委托给Presenter操作 |
SoC(Supervising Controller) | View自己负责UI处理以及数据绑定 |
2.7.代码飘过
相信刚接触MVP模式也许会像我一样,摸不清构建顺序吧。因为项目比较紧急,且又急于尝试MVP的实现,我仅在项目里使用了MVP+XUtils3的框架。下面贴出的代码仅供参考。
- 基础构建
public class BasePresenter<V> {
public V mvpView;
public Context mContext;
public BasePresenter(Context context, V mvpView) {
this.mContext = context;
this.mvpView = mvpView;
}
public void detachView() {
this.mvpView = null;
}
}
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity {
public P mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mvpPresenter = createPresenter();
}
protected abstract P createPresenter();
}
public abstract class BaseFragment<P extends BasePresenter> extends Fragement {
public P mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mvpPresenter = createPresenter();
}
protected abstract P createPresenter();
}
- 具体实现,这里以请求登录操作为例。先View interface后Presenter再Activity/Fragment(感觉顺些)
public interface LoginView {
void userLoginSuccess(UserInfo userInfo);
void userLoginFail(String failMsg);
}
public class LoginPresenter extends BasePresenter<LoginView> {
public void loginUser(String account, String pwd) {
UserLogin userLogin = new UserLogin();
userLogin.Account = account;
userLogin.Password = pwd;
RequestParams params = new RequestParams(ApiConfig.USER_IP + ApiConfig.USER_LOGIN);
params.setBodyContent(userLogin.toJSONString());
// 请求结果需对mvpView判空处理
x.http().post(params, new Callback.CommonCallback<UserInfo>() {
@Override
public void onSuccess(UserInfo model) {
if (null == mvpView) return;
if (model.getStatusCode() == 200) {
mvpView.userLoginSuccess(model.getData());
} else {
mvpView.userLoginFail(model.getMessage());
}
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
if (null == mvpView) return;
mvpView.userLoginFail("请求出错/网络异常");
}
@Override
public void onCancelled(CancelledException cex) {}
@Override
public void onFinished() {}
});
}
}
@ContentView(R.layout.activity_login)
public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView {
@ViewInject(R.id.login_edt_id)
private EditText mIdEdt;
@ViewInject(R.id.login_edt_pwd)
private EditText mPwdEdt;
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(this, this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
x.view().inject(this);
}
@Event(value = {R.id.login_btn}, type = View.OnClickListener.class)
private void onAppClick(View view) {
String idStr = mIdEdt.getText().toString();
String pwdStr = mPwdEdt.getText().toString();
// 为了代码简洁些,判空等处理被舍弃了
mvpPresenter.loginUser(idStr, pwdStr);
}
@Override
public void userLoginSuccess(UserInfo userInfo) {
// 登录成功,执行相应操作
}
@Override
public void userLoginFail(String failMsg) {
// 登录失败,Toast提示错误信息
}
}
以上只是个人实践内容,其实际上还有很多与MVP结合使用的大写之作,推荐:
MVVM
3.1.介绍
3.2.当然还是介绍
以上绘制并介绍了MVVM层与层之间的交互过程,其左边描述View与数据源的绑定,即控件显示数据与绑定的对象数据同步更新(可以忽略时序图的前后顺序这一说,只怪提供的绘图方法有限)。下面具体整理下各个层的含义与作用域:
who | what | where |
---|---|---|
Model | 数据处理层 | 数据的检索,存储等操作 |
View | 界面显示层 | Activity、Fragment、布局文件.xml |
ViewModel | 视图模型 | 可理解为View的Model和Presenter之间的融合 |
3.3.潜在输出
- 数据捆绑UI更新。数据与业务逻辑处在一个独立的ViewModel当中,由数据主导UI的更新(eg. DataBinding框架实现被捆绑的UI更新);
- 低耦合度。ViewModel不对View的任何控件保持持有状态,更改UI无需修改ViewModel的实现;
- 团队协作。扮演UI处理以及数据和业务逻辑的两个不同角色;
- 复用性强。同一个ViewModel可以为多个View服务;
- 单元测试。只需要在ViewModel执行单元测试,完全不需要依赖View的任何操作;
3.4.承受伤害
- 多个View与ViewModel执行捆绑,造成ViewModel比较笨重;
- 数据对象的直接引用使得后期维护起来会比较困难;
3.5.这里木有代码
只是了解了下但还没有确切实践过MVVM,暂时也就没有代码飘过了
总结
4.1.共同点
who | what |
---|---|
View | 一直扮演者与用户交互的角色 |
Model | 因数据处理而忙碌着 |
4.2.不同点
who | what |
---|---|
Controller | 被动的负责将View的需求告知Model处理并让Model通知View更新 |
Presenter | 在Model前面拿下View的请求做一定的业务处理才给回Model做数据操作处理, 数据请求处理结束后再将Model数据返回给View并提示更新 |
ViewModel | 在Presenter的基础上绑定并实现了对View的部分数据操作 |
PS1.没有什么绝对好的开发模式,倘若没有规范好自身的代码,一切都将是空谈
PS2.以上内容若存在不足之处,还望大虾们指点一二