前言:设计模式( Design pattern )是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石同样。只有精通了设计模式,才敢说真正理解了软件工程。能够说,设计模式是每个架构师所必备的技能之一。想要精通设计模式,必需要先搞清楚设计模式的六大原则。java
核心思想:单一原则很简单,就是将一组相关性很高的函数、数据封装到一个类中。换句话说,一个类应该有职责单一。算法
问题描述:假若有类Class1完成职责T1,T2,当职责T1或T2有变动须要修改时,有可能影响到该类的另一个职责正常工做。编程
好处:类的复杂度下降、可读性提升、可维护性提升、扩展性提升、下降了变动引发的风险。设计模式
需注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化缘由”来衡量接口或类设计得是否优良,可是“职责”和“变化缘由”都是不能够度量的,因项目和环境而异。数组
核心思想:尽可能经过扩展软件实体来解决需求变化,而不是经过修改已有的代码来完成变化bash
通俗来说: 一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,咱们就应该在设计的时候尽可能适应这些变化,以提升项目的稳定性和灵活性。数据结构
核心思想:在使用基类的的地方能够任意使用其子类,能保证子类完美替换基类。架构
通俗来说:只要父类能出现的地方子类就能出现。反之,父类则未必能胜任。app
好处:加强程序的健壮性,即便增长了子类,原有的子类还能够继续运行。框架
需注意:若是子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。
核心思想:高层模块不该该依赖底层模块,两者都该依赖其抽象;抽象不该该依赖细节;细节应该依赖抽象;
说明:高层模块就是调用端,底层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。
通俗来说:依赖倒置原则的本质就是经过抽象(接口或抽象类)使个各种或模块的实现彼此独立,互不影响,实现模块间的松耦合。
问题描述:类A直接依赖类B,假如要将类A改成依赖类C,则必须经过修改类A的代码来达成。这种场景下,类A通常是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操做;假如修改类A,会给程序带来没必要要的风险。
解决方案:将类A修改成依赖接口interface,类B和类C各自实现接口interface,类A经过接口interface间接与类B或者类C发生联系,则会大大下降修改类A的概率。
好处:依赖倒置的好处在小型项目中很难体现出来。但在大中型项目中能够减小需求变化引发的工做量。使并行开发更友好。
核心思想:类间的依赖关系应该创建在最小的接口上
通俗来说:创建单一接口,不要创建庞大臃肿的接口,尽可能细化接口,接口中的方法尽可能少。也就是说,咱们要为各个类创建专用的接口,而不要试图去创建一个很庞大的接口供全部依赖它的类去调用。
问题描述:类A经过接口interface依赖类B,类C经过接口interface依赖类D,若是接口interface对于类A和类B来讲不是最小接口,则类B和类D必须去实现他们不须要的方法。 需注意:
接口尽可能小,可是要有限度。对接口进行细化能够提升程序设计灵活性,可是若是太小,则会形成接口数量过多,使设计复杂化。因此必定要适度
提升内聚,减小对外交互。使接口用最少的方法去完成最多的事情
为依赖接口的类定制服务。只暴露给调用的类它须要的方法,它不须要的方法则隐藏起来。只有专一地为一个模块提供定制服务,才能创建最小的依赖关系。
核心思想:类间解耦。
通俗来说: 一个类对本身依赖的类知道的越少越好。自从咱们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。不管是面向过程编程仍是面向对象编程,只有使各个模块之间的耦合尽可能的低,才能提升代码的复用率。低耦合的优势不言而喻,可是怎么样编程才能作到低耦合呢?那正是迪米特法则要去完成的。
开闭原则是总纲,他告诉咱们要对扩展开放,对修改关闭;
单一职责原则告诉咱们实现类要职责单一;
里氏替换原则告诉咱们不要破坏继承体系;
依赖倒置原则告诉咱们要面向接口编程;
接口隔离原则告诉咱们在设计接口的时候要精简单一;
迪米特法则告诉咱们要下降耦合。
定义
确保单例类只有一个实例,而且这个单例类提供一个函数接口让其余类获取到这个惟一的实例。
若是某个类,建立时须要消耗不少资源,即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模式的意思是,软件能够分红三个部分。
视图(View):用户界面。 控制器(Controller):业务逻辑 模型(Model):数据保存 各部分之间的通讯方式以下。
View 传送指令到 Controller Controller 完成业务逻辑后,要求 Model 改变状态 Model 将新的数据发送到 View,用户获得反馈
全部通讯都是单向的。
MVP 模式将 Controller 更名为 Presenter,同时改变了通讯方向。
各部分之间的通讯,都是双向的。
View 与 Model 不发生联系,都经过 Presenter 传递。
View 很是薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter很是厚,全部逻辑都部署在那里。
MVVM 模式将 Presenter 更名为 ViewModel,基本上与 MVP 模式彻底一致。
惟一的区别是,它采用双向绑定(data-binding):View的变更,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。