MVP设计模式(框架):为了解决复杂业务逻辑和UI展示的耦合问题而设计出来的。遵循该设计模式可以提高代码的可维护性、可扩展性和可重用性。
软件架构:是指软件系统的高层结构,包括各种组件,组件之间的关系。它定义了系统的基本结构、行为和属性,并提供维护指导。
组件:可以理解为独立的功能模块。若是单独独立出来都可以打包成库供其他程序使用。
可以看出来,MVP并不等价于软件架构。这篇文章主要介绍MVP设计模式中的对M(即model层)的一点点误解。
MVP设计模式实现原理
由上图可以看出MVP的核心是View、Presenter、Model 这3层。
View:负责显示UI界面和用户交互。它要实现UI更新相关的接口。
Model:负责业务逻辑和数据管理。实现model对外开放的功能接口。
Presenter:作为Model和View之间的中介,处理业务逻辑和UI逻辑的交互。
但是我们在真正应用实践MVP的时候,最好把相关接口也定义出来,并且一定要清楚它们存在的意义。
ui_interface: 该接口定义了UI的更新方法,由Presenter持有其实例。一般是model层执行完相关业务之后需要更新UI,此时通过Presenter调用该接口实例去执行UI更新。
model_interface: 该接口定义了模型层核心业务对外暴露的功能方法,一般用户操作相关UI交互后会执行model层的接口方法。
logical_interface: 逻辑接口(可以理解为用户行为接口),提供给View层执行相关UI交互逻辑。
既然已经有了model_interface 接口了,为什么还需要一个logical_interface 呢? 当UI界面仅对应一个独立的Model模块时 logical_interface 和 model_interface 可以认为是无差别的,但是当UI界面对应的Model超过1个时,业务逻辑就会变得复杂,一个UI交互可能涉及到多个Model的业务处理,此时就需要提供一个统一的业务逻辑接口供View层使用。试想一下,若没有这个logical_interface 接口的话,那对于复杂交互势必会向View层提供多个Model接口实例,在View层做业务逻辑的组合,这是不是又把View 和 Model耦合在一起了,是不是Presenter又失去其存在的意义了。
常见错误
错误1: 误把Model当作纯数据类
Model层表示核心业务逻辑和数据管理。最普遍的一个错误就是把Model 作为一个单纯的数据类提供给Presenter使用,并且将核心业务逻辑放在Presenter层。
这么设计会有2个致命的缺陷:
-
问题一:无法复用
这里请问一个问题:若核心业务逻辑在Presenter层,那怎么复用该业务逻辑呢? 我们都知道Presenter对应的是一个界面的展示器,它是一个界面的定制,我们很难在其他界面直接复用Presenter的。但是核心业务逻辑复用真的是每时每刻都存在,比如商店app的下载能力几乎在每个界面都会存在,难道要每个Presenter都实现一个下载能力吗?显示不是的,我们这种情况下一般都会将下载核心业务逻辑定义在下载Model中(即在下载模块中实现核心业务逻辑)。
所以这里我们应该把model 理解为模块的概念,它是我们app的核心功能模块,需要内聚该模块所有的业务逻辑,对外提供相关功能和需要对外提供的数据。
-
问题二:无法扩展
一般复杂点的UI大多同时涉及到多个model的业务交互。比如商店的UI界面就常常跟数据更新,下载,安装等多个模块交互。若所有业务放在Presenter中实现,那就意味着更新,下载,安装等业务耦合在一起了,下次要扩展下载功能,那势必会导致更新和安装业务也受到影响。
业务逻辑:指软件的核心功能流程。比如商店的下载业务,推送的长连接保活业务等等。
错误2: model层很少提供对外接口
在没有接口定义的情况下,要理解model层的核心业务功能有哪些?那就势必要去了解具体的实现类中去查询,若具体实现还不是内聚在一个类中的时候,那就很要命了,这非常不利于核心业务的维护,model具体提供了哪些业务功能可能只有作者一个人知道(可能有时候自己也不记得那么多)。
所以,上述问题我们可以通过定义一个业务接口来进行维护,能够减少很多不必要的维护成本,也能保证你的业务逻辑实现一定是内聚的(因为要接口实现必定是在一个类中完成的)。
错误3: 数据没有内聚
将model层数据细节全部对外暴露,所有数据均提供set方法,具体在哪里set,我们其实是并不知情的,这也带来了极大的风险,给维护带来了极大的不确定性。
这里我们应该在数据类中定义业务方法来修改相关属性,而非提供具体的set方法供其他人使用。比如下面的例子:
class Person() {
private String mName;
private int mAge;
public Person(String name, int age) {
mName = name;
mAge = age;
}
public void happyBirthday() {
mAge++;
}
}
如上示例中,我们定义了Person类,其提供name 和 age 2个属性。其中name一旦确定就不允许再次设置,而age也没有提供set方法,而是只能在happyBirthday方法中进行+1操作。 这么设计的好处就是我们的数据是完全可控的,我们知道它一定只会在过生日场景下发生变化。若是直接提供set方法任由上层业务去调用,那将充满不确定性,你不知道谁会在什么场景下随意调用set方法来满足他的某个需求。
总结
这篇文章我们简单介绍了MVP设计模式,并且简单介绍了model层的错误使用场景:
- model层仅定义数据,没有核心业务逻辑业务。
- model层没有提供核心业务接口,实现不内聚的情况下会很难维护。
- 数据不够内聚。我们一定要严格控制数据的可变性设置,不轻易开放set方法,更多的是通过提供业务方法来修改数据属性。
欢迎大家留言讨论大家在实践MVP设计模式时遇到的常见问题。