设计模式 六大原则
前言:设计模式( Design pattern )是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。只有精通了设计模式,才敢说真正理解了软件工程。可以说,设计模式是每一个架构师所必备的技能之一。想要精通设计模式,必须要先搞清楚设计模式的六大原则。
单一职责原则(Single Responsibility Principle,简称SRP )
核心思想:单一原则很简单,就是将一组相关性很高的函数、数据封装到一个类中。换句话说,一个类应该有职责单一。
问题描述:假如有类Class1完成职责T1,T2,当职责T1或T2有变更需要修改时,有可能影响到该类的另外一个职责正常工作。
好处:类的复杂度降低、可读性提高、可维护性提高、扩展性提高、降低了变更引起的风险。
需注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可以度量的,因项目和环境而异。
开放封闭原则(Open Close Principle,简称OCP)
核心思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
通俗来讲: 一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
里氏替换原则(Liskov Substitution Principle,简称LSP)
核心思想:在使用基类的的地方可以任意使用其子类,能保证子类完美替换基类。
通俗来讲:只要父类能出现的地方子类就能出现。反之,父类则未必能胜任。
好处:增强程序的健壮性,即使增加了子类,原有的子类还可以继续运行。
需注意:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。
依赖倒置原则(Dependence Inversion Principle,简称DIP)
核心思想:高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;
说明:高层模块就是调用端,底层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。
通俗来讲:依赖倒置原则的本质就是通过抽象(接口或抽象类)使个各类或模块的实现彼此独立,互不影响,实现模块间的松耦合。
问题描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口interface,类B和类C各自实现接口interface,类A通过接口interface间接与类B或者类C发生联系,则会大大降低修改类A的几率。
好处:依赖倒置的好处在小型项目中很难体现出来。但在大中型项目中可以减少需求变化引起的工作量。使并行开发更友好。
接口隔离原则(Interface Segregation Principle,简称ISP)
核心思想:类间的依赖关系应该建立在最小的接口上
通俗来讲:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
问题描述:类A通过接口interface依赖类B,类C通过接口interface依赖类D,如果接口interface对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。 需注意:
接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情
为依赖接口的类定制服务。只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
迪米特法则(Law of Demeter,简称LoD)
核心思想:类间解耦。
通俗来讲: 一个类对自己依赖的类知道的越少越好。自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
- 一句话概括:
开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭;
单一职责原则告诉我们实现类要职责单一;
里氏替换原则告诉我们不要破坏继承体系;
依赖倒置原则告诉我们要面向接口编程;
接口隔离原则告诉我们在设计接口的时候要精简单一;
迪米特法则告诉我们要降低耦合。
设计模式
单例模式
定义
确保单例类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。
如果某个类,创建时需要消耗很多资源,即new出这个类的代价很大;或者是这个类占用很多内存,如果创建太多这个类实例会导致内存占用太多。上述情况下就应该使用单例模式
实际应用
//获取WindowManager服务引用
WindowManager wm = (WindowManager) getSystemService(getApplication().WINDOW_SERVICE);
其内部就是通过单例的方式持有一个WindowManager并返回这个对象
建造者模式
定义
将一个复杂对象的构造与它的表示分离,使得同样的构造过程可以创建不同的表示。 主要是在创建某个对象时,需要设定很多的参数(通过setter方法),但是这些参数必须按照某个顺序设定,或者是设置步骤不同会得到不同结果。
示例
各类自定义Dialog
AlertDialog.Builer builder=new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon)
.setTitle("title")
.setMessage("message")
.setPositiveButton("Button1",
new DialogInterface.OnclickListener(){
public void onClick(DialogInterface dialog,int whichButton){
setTitle("click");
}
})
.create()
.show();
原型模式
定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。 可以在类的属性特别多,但是又要经常对类进行拷贝的时候可以用原型模式,这样代码比较简洁,而且比较方便。
拷贝时要注意浅拷贝与深拷贝
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
//克隆副本
Intent copyIntent=(Intetn)shareIntent.clone();
简单工厂模式
定义
建立一个工厂(一个函数或一个类方法)来制造新的对象。
public Object getSystemService(String name) {
if (getBaseContext() == null) {
throw new IllegalStateException("System services not available to Activities before onCreate()");
}
//........
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
//.......
return super.getSystemService(name);
}
在getSystemService方法中就是用到了简单工厂模式,根据传入的参数决定创建哪个对象,由于这些对象以单例模式提前创建好了,所以此处不用new了,直接把单例返回就好。
工厂方法模式
定义
是定义一个创建产品对象的工厂接口,让其子类决定实例化哪一个类,将实际创建工作推迟到子类当中。
public abstract class Product {
public abstract void method();
}
public class ConcreteProduct extends Prodect {
public void method(){
System.out.println("我是具体产品!");
}
}
public abstract class Factory{
public abstract Product createProduct();
}
public class ConcreteFactory extends Factory{
public Product createProduct(){
return new ConcreteProductA();
}
}
抽象工厂模式
抽象工厂模式:为创建一组相关或者是相互依赖的对象提供一个接口,而不需要制定他们的具体类 看个例子吧,将它跟工厂方法模式做个对比:
public abstract class AbstractProductA{
public abstract void method();
}
public abstract class AbstractProdectB{
public abstract void method();
}
public class ConcreteProductA1 extends AbstractProductA{
public void method(){
System.out.println("具体产品A1的方法!");
}
}
public class ConcreteProductA2 extends AbstractProductA{
public void method(){
System.out.println("具体产品A2的方法!");
}
}
public class ConcreteProductB1 extends AbstractProductB{
public void method(){
System.out.println("具体产品B1的方法!");
}
}
public class ConcreteProductB2 extends AbstractProductB{
public void method(){
System.out.println("具体产品B2的方法!");
}
}
public abstract class AbstractFactory{
public abstract AbstractProductA createProductA();
public abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory{
public AbstractProductA createProductA(){
return new ConcreteProductA1();
}
public AbstractProductB createProductB(){
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory{
public AbstractProductA createProductA(){
return new ConcreteProductA2();
}
public AbstractProductB createProductB(){
return new ConcreteProductB2();
}
}
其实Android源码中对抽象工厂出现的比较少,好在抽象工厂方法并不复杂,很容易记住,我们可以从Service中去理解,Service的onBind方法可以看成是一个工厂方法,从framework角度来看Service,可以看成是一个具体的工厂,这相当于一个抽象工厂方法模式的雏形。
public class BaseService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent){
return new Binder();
}
}
策略模式
定义
有一系列的算法,将每个算法封装起来(每个算法可以封装到不同的类中),各个算法之间可以替换,策略模式让算法独立于使用它的客户而独立变化。
public abstract class BaseAdvertManager {
protected abstract void doLoadAdvert();
}
public class FacebookAdvertManager extends BaseAdvertManager {
@Override
protected void doLoadAdvert() {
Log.v(TAG, "加载Facebook广告");
}
}
public class AdmobAdvertManager extends BaseAdvertManager {
@Override
protected void doLoadAdvert() {
Log.v(TAG, "加载Admob广告");
}
}
Android在属性动画中使用时间插值器的时候就用到了策略模式。在使用动画时,你可以选择线性插值器LinearInterpolator、加速减速插值器AccelerateDecelerateInterpolator、减速插值器DecelerateInterpolator以及自定义的插值器。这些插值器都是实现根据时间流逝的百分比来计算出当前属性值改变的百分比。通过根据需要选择不同的插值器,实现不同的动画效果。
状态模式
定义
状态模式中,行为是由状态来决定的,不同状态下有不同行为。状态模式和策略模式的结构几乎是一模一样的,主要是他们表达的目的和本质是不同。
public interface TvState{
public void nextChannerl();
public void prevChannerl();
public void turnUp();
public void turnDown();
}
public class PowerOffState implements TvState{
public void nextChannel(){}
public void prevChannel(){}
public void turnUp(){}
public void turnDown(){}
}
public class PowerOnState implements TvState{
public void nextChannel(){
System.out.println("下一频道");
}
public void prevChannel(){
System.out.println("上一频道");
}
public void turnUp(){
System.out.println("调高音量");
}
public void turnDown(){
System.out.println("调低音量");
}
}
public interface PowerController{
public void powerOn();
public void powerOff();
}
public class TvController implements PowerController{
TvState mTvState;
public void setTvState(TvStete tvState){
mTvState=tvState;
}
public void powerOn(){
setTvState(new PowerOnState());
System.out.println("开机啦");
}
public void powerOff(){
setTvState(new PowerOffState());
System.out.println("关机啦");
}
public void nextChannel(){
mTvState.nextChannel();
}
public void prevChannel(){
mTvState.prevChannel();
}
public void turnUp(){
mTvState.turnUp();
}
public void turnDown(){
mTvState.turnDown();
}
}
public class Client{
public static void main(String[] args){
TvController tvController=new TvController();
tvController.powerOn();
tvController.nextChannel();
tvController.turnUp();
tvController.powerOff();
//调高音量,此时不会生效
tvController.turnUp();
}
}
Android源码中很多地方都有用到状态模式,举一个例子,就是Android的WIFI管理模块。当WIFI开启时,自动扫描周围的接入点,然后以列表的形式展示;当wifi关闭时则清空。这里wifi管理模块就是根据不同的状态执行不同的行为。
与策略模式的区别
状态模式的行为是平行的、不可替换的,策略模式是属于对象的行为模式,其行为是彼此独立可相互替换的。
责任链模式
定义
使多个对象都有机会处理请求,从而避免请求的发送者和接受者直接的耦合关系,将这些对象连成一条链,并沿这条链传递该请求,直到有对象处理它为止。
/**
* 抽象处理者
*/
public abstract class Handler {
/**
* 持有后继的责任对象
*/
protected Handler successor;
/**
* 示意处理请求的方法,虽然这个示意方法是没有传入参数的
* 但实际是可以传入参数的,根据具体需要来选择是否传递参数
*/
public abstract void handleRequest();
/**
* 取值方法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 赋值方法,设置后继的责任对象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
/**
* 具体处理者
*/
public class ConcreteHandler extends Handler {
/**
* 处理方法,调用此方法处理请求
*/
@Override
public void handleRequest() {
/**
* 判断是否有后继的责任对象
* 如果有,就转发请求给后继的责任对象
* 如果没有,则处理请求
*/
if(getSuccessor() != null)
{
System.out.println("放过请求");
getSuccessor().handleRequest();
}else
{
System.out.println("处理请求");
}
}
}
/**
* 发起请求的客户类
*/
public class Client {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler();
Handler handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
//提交请求
handler1.handleRequest();
}
}
在Android处理点击事件时,父View先接收到点击事件,如果父View不处理则交给子View,把责任依次往下传递;还有Java的异常捕获机制也是责任链模式的一种体现
解释器模式
定义
给定一个语言,定义它的语法,并定义一个解释器,这个解释器用于解析语言。
这个用到的地方也不少,其一就是Android的四大组件需要在AndroidManifest.xml中定义,其实AndroidManifest.xml就定义了,等标签(语句)的属性以及其子标签,规定了具体的使用(语法),通过PackageManagerService(解释器)进行解析。
命令模式
定义
命令模式将每个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;将请求进行排队或者记录请求日志,以及支持可撤销操作。 举个例子来理解:当我们点击“关机”命令,系统会执行一系列操作,比如暂停事件处理、保存系统配置、结束程序进程、调用内核命令关闭计算机等等,这些命令封装从不同的对象,然后放入到队列中一个个去执行,还可以提供撤销操作。
在Android事件机制中,底层逻辑对事件的转发处理。每次的按键事件会被封装成NotifyKeyArgs对象,通过InputDispatcher封装具体的事件操作。还有一个例子就是我们使用的Runnable,我们可以使用它来封装自己想做的操作,然后交给Handler按顺序处理,或者在处理前remove取消掉
观察者模式
定义
有时被称作发布/订阅模式,其定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
ListView的适配器有个notifyDataSetChange()函数,就是通知ListView的每个Item,数据源发生了变化,各个子Item需要重新刷新一下。
备忘录模式
定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态,这样,以后就可将对象恢复到原先保存的状态中。
Activity的onSaveInstanceState和onRestoreInstanceState就是用到了备忘录模式,分别用于保存和恢复。
迭代器模式
定义
提供一种方法顺序访问一个容器对象中的各个元素,而不需要暴露该对象的内部表示。 迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。
Java的Iterator就是一个抽象迭代器,通过实现这个接口各个集合可以提供自己定制的具体迭代器,感兴趣的可以直接查看源码 虽然我们使用集合的场景非常多,但是实际使用到迭代器的却比较少,对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,比如ArrayList,我们更倾向于使用for循环来遍历,但是针对hash表之类的集合来说,引入迭代器就反而简单多了。同时我们也可以通过自定义迭代器来对有序列表提供正序遍历或者倒序遍历,用户只需要得到迭代器就可以遍历了,而不需要关心具体遍历算法。
Android源码中,最典型的就是Cursor用到了迭代器模式,当我们使用SQLiteDatabase的query方法时,返回的就是Cursor对象,之后再通过Cursor去遍历数据
cursor.moveToFirst();
do{
//cursor.getXXX(int);
}while(cursor.moveToNext);
模板方法模式
定义
定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定的步骤。
我们知道,启动一个Activity过程非常复杂,如果让开发者每次自己去调用启动Activity过程无疑是一场噩梦。好在启动Activity大部分代码时不同的,但是有很多地方需要开发者定制。也就是说,整体算法框架是相同的,但是将一些步骤延迟到子类中,比如Activity的onCreate、onStart等等。这样子类不用改变整体启动Activity过程即可重定义某些具体的操作了~。
访问者模式
定义:封装一些作用于某种数据结构中各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
访问者模式是23种设计模式中最复杂的一个,但他的使用率并不高,大部分情况下,我们不需要使用访问者模式,少数特定的场景才需要。
Android中运用访问者模式,其实主要是在编译期注解中,编译期注解核心原理依赖APT(Annotation Processing Tools),著名的开源库比如ButterKnife、Dagger、Retrofit都是基于APT。
中介者模式
定义:中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显调用,从而使他们可以轻松耦合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用保证这些作用可以彼此独立的变化,中介者模式将多对多的相互作用转为一对多的相互作用。
什么时候用中介者模式呢?其实,中介者对象是将系统从网状结构转为以调停者为中心的星型结构。
举个简单的例子,一台电脑包括:CPU、内存、显卡、IO设备。其实,要启动一台计算机,有了CPU和内存就够了。当然,如果你需要连接显示器显示画面,那就得加显卡,如果你需要存储数据,那就要IO设备,但是这并不是最重要的,它们只是分割开来的普通零件而已,我们需要一样东西把这些零件整合起来,变成一个完整体,这个东西就是主板。主板就是起到中介者的作用,任何两个模块之间的通信都会经过主板协调。
那么Android中那些地方用到了中介者模式呢?在Binder机制中,就用到了中介者模式,对Binder不是很熟悉的童鞋请参考我的《 简单明了,彻底地理解Binder》。我们知道系统启动时,各种系统服务会向ServiceManager提交注册,即ServiceManager持有各种系统服务的引用 ,当我们需要获取系统的Service时,比如ActivityManager、WindowManager等(它们都是Binder),首先是向ServiceManager查询指定标示符对应的Binder,再由ServiceManager返回Binder的引用。并且客户端和服务端之间的通信是通过Binder驱动来实现,这里的ServiceManager和Binder驱动就是中介者。
代理模式
定义:为其他类提供一种代理以控制这个对象的访问。
其实代理模式我们平时用的也比较多,其实比较好理解,就是当我们需要对一个对象进行访问时,我们不直接对这个对象进行访问,而是访问这个类的代理类,代理类能帮我们执行我们想要的操作。代理模式比较容易理解,既然你来看这篇文章相信你对代理模式不陌生。
我们直接看看代理模式在Android中的应用,如果你查看AIDL生成的代码就知道,它会根据当前的线程判断是否要跨进程访问,如果不需要跨进程就直接返回实例,如果需要跨进程则返回一个代理,这个代理干什么事情呢?我们在《 简单明了,彻底地理解Binder》提到,在跨进程通信时,需要把参数写入到Parcelable对象,然后再执行transact函数,我们要写的代码挺多的。AIDL通过生成一个代理类,代理类中自动帮我们写好这些操作。
组合模式
定义:将对象组成成树形结构,以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
上面的定义不太好理解,我们直接从Android中用到的组合模式说起。我们知道,Android中View的结构是树形结构,每个ViewGroup包含一系列的View,而ViewGroup本身又是View。这是Android中非常典型的组合模式。
适配器模式
定义:把一个类的接口变换成客户端所期待的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
其实适配器模式很容易理解,我们在Android开发时也经常用到。比较典型的有ListView和RecyclerView。为什么ListView需要使用适配器呢?主要是,ListView只关心它的每个ItemView,而不关心这个ItemView具体显示的是什么。而我们的数据源存放的是要显示的内容,它保存了每一个ItemView要显示的内容。ListView和数据源之间没有任何关系,这时候,需要通过适配器,适配器提供getView方法给ListView使用,每次ListView只需提供位置信息给getView函数,然后getView函数根据位置信息向数据源获取对应的数据,根据数据返回不同的View。
装饰模式
定义:动态的给一个对象添加额外的智者,就增加功能来说,装饰模式比子类继承的方式更灵活。 通过简单代码来理解装饰模式:
public abstract class Component{
public abstract void operate();
}
public class ConcreteComponent extends Component{
public void operate(){
//具体的实现
}
}
public class Decorator{
private Component component;
public Decorator(Component component){
this.component=component;
}
public void operate(){
operateA();
component.operate();
operateB();
}
public void operateA(){
//具体操作
}
public void operateB(){
//具体操作
}
}
那么在Android哪里出现了装饰模式呢?我们平时经常用到Context类,但是其实Context类只是个抽象类,具体实现是ContextImpl,那么谁是ContextImpl的装饰类呢?我们知道Activity是个Context,但是Activity 并不是继承于Context,而是继承于ContextThremeWrapper.而ContextThremeWrapper继承于ContextWrapper,ContextWrapper继承Context.说了这么多,跟装饰模式有啥关系?主要是引入ContextWrapper这个类。ContextWrapper内部有个Context引用mContext,并且ContextWrapper中对Context的每个方法都有实现,在实现中调用的就是mContext相同的方法。
享元模式
定义:使用享元对象有效地支持大量的细粒度对象。
享元模式我们平时接触真的很多,比如Java中的常量池,线程池等。主要是为了重用对象。
在Android哪里用到了享元模式呢?线程通信中的Message,每次我们获取Message时调用Message.obtain()其实就是从消息池中取出可重复使用的消息,避免产生大量的Message对象。
外观模式
定义:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。
怎么理解呢,举个例子,我们在启动计算机时,只需按一下开关键,无需关系里面的磁盘、内存、cpu、电源等等这些如何工作,我们只关心他们帮我启动好了就行。实际上,由于里面的线路太复杂,我们也没办法去具体了解内部电路如何工作。主机提供唯一一个接口“开关键”给用户就好。
那么Android哪里使用到了外观模式呢?依然回到Context,Android内部有很多复杂的功能比如startActivty、sendBroadcast、bindService等等,这些功能内部的实现非常复杂,如果你看了源码你就能感受得到,但是我们无需关心它内部实现了什么,我们只关心它帮我们启动Activity,帮我们发送了一条广播,绑定了Activity等等就够了。
桥接模式
定义:将抽象部分与实现部分分离,使他们独立地进行变化。 其实就是,一个类存在两个维度的变化,且这两个维度都需要进行扩展。
在Android中桥接模式用的很多,举个例子,对于一个View来说,它有两个维度的变化,一个是它的描述比如Button、TextView等等他们是View的描述维度上的变化,另一个维度就是将View真正绘制到屏幕上,这跟Display、HardwareLayer和Canvas有关。这两个维度可以看成是桥接模式的应用。
设计架构 MVC、MVP、MVVM
MVC
MVC模式的意思是,软件可以分成三个部分。
视图(View):用户界面。 控制器(Controller):业务逻辑 模型(Model):数据保存 各部分之间的通信方式如下。
View 传送指令到 Controller Controller 完成业务逻辑后,要求 Model 改变状态 Model 将新的数据发送到 View,用户得到反馈
所有通信都是单向的。
MVP
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
-
各部分之间的通信,都是双向的。
-
View 与 Model 不发生联系,都通过 Presenter 传递。
-
View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
MVVM
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。
唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。