android开发中使用到的一些设计者模式- http://blog.csdn.net/xiangzhihong8/article/details/28593827
引用: http://m.blog.csdn.net/blog/index?username=qq_17766199 http://blog.csdn.net/stefzeus/article/details/7503749
书籍:何红辉《Android源码设计模式解析与实战》
引用:http://blog.csdn.net/column/details/mode.html?&page=2
>>> 1.单例模式
单例模式应该是日常使用最为广泛的一种模式了。他的作用是确保某个类只有一个实例,避免产生多个对象消耗过多的资源。比如对数据库的操作时,就可以使用单例模式。
Android源码中的单例模式运用:在Android系统中,我们经常会通过Context获取系统级别的服务,如WindowsManagerService、ActivityManagerService等,更常用的是一个LayoutInflater的类,这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取。
总结:
优点:
(1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
(2)单例模式可以避免对资源的多重占用,例如一个文件操作,由于只有一个实例存在内存中,避免对同一资源文件的同时操作。
(3)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。
缺点:
(1)单例模式一般没有接口,扩展很困难,若要扩展,只能修改代码来实现。
(2)单例对象如果持有Context,那么很容易引发内存泄露。此时需要注意传递给单例对象的Context最好是Application Context。
Android开发中单例模式写法与可能遇到的坑- http://blog.csdn.net/chenkai19920410/article/details/54612505
>>> 2. Builder模式
1.定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2.使用场景
(1)相同的方法,不同的执行顺序,产生不同的事件结果时。
(2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
(3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个使用建造者模式非常适合。
(4)当初始化一个对象特别复杂时,如参数多,且很多参数有默认值。
Android源码中的Builder模式运用:AlertDialog.Builder
总结:
优点:
(1)良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成细节。
(2)建造者独立,容易扩展。
缺点:
(1)会产生多余的Builder对象及Director对象,消耗内存。
>> 3. 原型模式
1、定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。被复制的实例就是“原型”,这个原型是可定制的。
2、使用场景
(1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
(2)通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
(3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
Android源码中的原型模式:Intent
总结:
优点
(1)原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可能更好的体现其优点。
(2)还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。
缺点:
(1)这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
(2)通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升。
>> 4.工厂方法模式
1.定义
定义一个用于创建对象的接口,让子类决定实例化那个类。
2.使用场景
在任何需生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式。
Android源码中的工厂方法模式:1.Activity的各种生命周期;2.ArrayList和HashSet
总结
优点:
1.工厂方法模式完全符合设计原则,降低了对象之间的耦合。高层模块只需要知道产品的抽象类,其他的实现都不需要关心。
2.良好的封装性,代码结构清晰。扩展性好。
缺点:
每次我们为工厂方法模式添加新的产品时就要编写一个新的产品类。同时还要引入抽象层,这必然会导致类结构的复杂化,所以,在某些情况比较简单时,是否要使用工厂模式,需要设计者权衡利弊了。
>> 5. 抽象工厂模式
简单运用:以车厂生产汽车零部件为例,A、B两家车厂分别生产不同的轮胎、发动机、制动系统。虽然生产的零件不同,型号不同。但是根本上都有共同的约束,就是轮胎、发动机、制动系统。
1.定义
为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定他们的具体实现类。
2.使用场景
一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。例如一个文本编辑器和一个图片处理器,都是软件实体,但是Linix下的文本编辑器和WINDOWS下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也是类似情况,也就是具有了共同的约束条件:操作系统类型,于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。
Android源码中的实现:
抽象工厂模式在Android源码中使用较少,因为很少会出现多个产品种类的情况,大部分使用工厂方法模式即可解决。
1.MediaPlayer: MediaPlayer Factory分别会生成4个不同的MediaPlayer基类:StagefrightPlayer、NuPlayerDriver、MidiFile和TestPlayerStub,四者均继承于MediaPlayerBase。
总结
优点:
分离接口与实现,客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已,使其从具体的产品实现中解耦,同时基于接口与实现分离,使抽象该工厂方法模式在切换产品类时更加灵活、容易。
缺点:
一是对类文件的爆炸性增加,二是不太容易扩展新的产品类。
>> 6. 策略模式
通常如果一个问题有多个解决方案时,最简单的就是利用if-else或者switch-case方式根据不同的情景选择不同的解决方案,但是这样耦合性太高 、代码臃肿、难以维护等。这时就可以使用策略模式来解决。
1.定义
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
2.使用场景
1.针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
2.需要安全地封装多种同一类型的操作时。
3.出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。
Android源码中的策略模式实现:1.时间插值器(TimeInterpolator),LinearInterpolator、AccelerateInterpolator、CycleInterpolator等实现Interpolator,通过getInterpolator(float input)获取当前的时间百分比,以此来计算动画的属性值。
总结
策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好地演示了开闭原则,也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。
优点:
1.结构清晰明了、使用简单直观。
2.耦合度相对而言较低,扩展方便。
3.操作封装也更为彻底,数据更为安全。
缺点:
1.随着策略的增加,子类也会变得繁多。
>> 7.状态模式
1.定义
状态模式中的行为是由状态来决定,不同的状态下有不同的行为。当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
2.使用场景
1.一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
2.代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有大量的多分支语句,且这些分支依赖于该对象的状态。
3简单实现
实现效果:首先将电视的状态分为开机与关机状态,开机时可以通过遥控器实现频道切换和调节音量,但是关机时,这些操作都会失效。
4.与策略模式的区别
状态模式与策略模式的结构几乎是一样的,就像是孪生兄弟。但是他们的目地、本质不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立的、可相互替换的。状态模式,通常是自我控制状态的改变。而策略模式,是由外部指定使用什么样的策略。
5.Android实战中的使用:
1.登录系统,根据用户是否登录,判断事件的处理方式。
2.Wi-Fi管理,在不同的状态下,WiFi的扫描请求处理不一。
6.总结
1.优点
将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。
2.缺点
状态模式的使用必然会增加系统类和对象的个数。
>> 8.责任链模式
1.定义
责任链模式是行为型设计模式之一,它使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
2.使用场景
1.多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
2.在请求处理者不明确的情况下向多个对象中的一个提交请求。
3.需要动态指定一组对象处理请求。
3.简单实现
我们在公司有各种原因需要报销费用,首先我们要找我们的上级领导去审批,报销额度如果在领导的权限范围内,那就审批通过,否则领导在找自己的上级去审批,以此类推。
对于责任链中的一个处理者对象,有两个行为。一是处理请求,二是将请求传递到下一节点,不允许某个处理者对象在处理了请求后又将请求传送给上一个节点的情况。
对于一条责任链来说,一个请求最终只有两种情况。一是被某个处理对象所处理,另一个是所有对象均未对其处理,对于前一种情况我们称为纯的责任链模式,后一种为不纯的责任链。实际中大多为不纯的责任链。
4.Android源码中的责任链模式:1.View事件的分发处理
ViewGroup事件投递的递归调用就类似于一条责任链,一旦其寻找到责任者,那么将由责任者持有并消费掉该次事件,具体体现在View的onTouchEvent方法中返回值的设置,如果返回false,那么意味着当前的View不会是该次的责任人,将不会对其持有;如果返回true,此时View会持有该事件并不再向外传递。
5.总结
1.优点
可以对请求者和处理者的关系解耦,提高代码的灵活性。
2.缺点
每次都需要对链中请求处理者遍历,如果处理者太多那么遍历必定会影响性能,特别是在一些递归调用者中,要慎用。
>>> 9.解释器模式
解释器模式是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式。但是它的使用场景确实很广泛,只是因为我们自己很少回去构造一个语言的文法,所以使用较少。
1.定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。(其中语言就是我们需要解释的对象,文法就是这个语言的规律,解 释器就是翻译机,通过文法来翻译语言。)
2.使用场景
1.如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象的语法树时可以考虑使用解释器模式。
2.在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。
3.简单实现
我们使用解释器模式对“m+n+p”这个表达式进行解释,那么代表数字的m、n和p就可以看成终结符号,而“+”这个运算符号可以当做非终结符号。
TerminalExpression:终结符表达式,实现文法中与终结符有关的解释操作。文法中每个终结符都有一个具体的终结表达式与之对应。
NonterminalExpression :非终结符表达式,实现文法中与非终结符有关的解释操作。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
Android源码中的模式实现:1.PackageParser
PackageParser是对AndroidManifest.xml配置文件进行读取的,具体原理参考:解析AndroidManifest原理
5.总结
1.优点
最大的优点使其灵活的扩展性,当我们想对文法规则进行扩展延伸时,只需要增加相应的非终结符解释器,并在构建抽象语法树时,使用到新增的解释器对象进行具体的解释即可,非常方便。
2.缺点
1.每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
2.解释器模式由于使用了大量的循环和递归,效率是个问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。
>> 10.命令模式
命令模式是行为型模式之一。总体来说并不难理解,只是比较繁琐,他会将简单的调用关系解耦成多个部分,增加类的复杂度,但是即便如此,命令模式的结构依然清晰。
1.定义
将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
2.使用场景
(1)需要抽出待执行的动作,然后以参数的形式提供出来。
(2)在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。
(3)需要支持操作取消。
(4)支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍。
(5)需要支持事务操作。
3.简单实现
以推箱子游戏为例,一般游戏中会有五个按钮,分别是左移、右移、下移、上移和撤销。那么玩游戏的人就是客户端,五个按钮就是调用者,执行具体按钮命令的方法是命令角色。
设计模式的使用之前也有提到,主要是要看当前场景的复杂度和以后的需求进行扩展、维护等方面,完全使用设计模式也是不提倡的,这就需要设计者权衡利弊了。
4.Android源码中的命令模式实现:1.PackageHandler
PackageManagerService中,其对包的相关消息处理右其内部类PackageHandler承担,其将需要处理的请求作为对象通过消息传递给相关的方法,而对于包的安装、移动以及包大小的测量则分别封装为HandlerParams的具体子类InstallParams、MoveParams和MeasureParams。HandlerParams也是一个抽象命令者。
5.总结
1.优点
命令模式的封装性很好,更弱的耦合性,更灵活的控制性以及更好的扩展性。
2.缺点
类的膨胀,大量衍生类的创建。
>> 11.观察者模式
观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目
EventBus、Otto、AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式。
1.定义
观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
2.使用场景
(1)关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
(2)事件多级触发场景。
(3)跨系统的消息交换场景,如消息队列、事件总线的处理机制。
3.简单实现
这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们
Android源码中的观察者模式:1.BaseAdapter
BaseAdapter我相信大家都不陌生,在ListView的适配器中我们都是继承它。
5.总结
1.优点
(1)观察者和被观察者之间是抽象耦合,应对业务变化。
(2)增强系统的灵活性和可扩展性。
2.缺点
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
>> 12.备忘录模式
备忘录模式是一种行为模式,该模式用于保存对象当前的状态,并且在之后可以再次恢复到此状态,有点像是我们平常说的”后悔药”。
1.定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。
2.使用场景
(1)需要保存一个对象在某一个时刻的状态或部分状态。
(2)如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。
3.简单实现
书中例子:以”使命召唤”游戏为例,用游戏中的存档功能来举例。
Android源码中的备忘录模式:1.onSaveInstanceState和onRestoreInstanceState
当Activity不是正常方式退出,且Activity在随后的时间内被系统杀死之前会调用这两个方法让开发人员可以有机会存储Activity相关信息,且在下次返回Activity时恢复这些数据。通过这两个函数。开发人员能够在某些特殊场景下储存与界面相关的信息,提升用户体验。
基本使用参考:链接
5.总结
1.优点
(1)给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史状态。
(2)实现了信息的封装,使用户不需要关心状态的保存细节。
2.缺点
消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
>> 13.迭代器模式
迭代器模式,又叫做游标模式,是行为型设计模式之一。我们知道对容器对象的访问必然会涉及遍历算法,我们可以将遍历的方法封装在容器中,或者不提供遍历方法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。
然而在前一种情况,容器承受了过多的功能,它不仅要负责自己“容器”内的元素维护(添加、删除等等),而且还要提供遍历自身的接口;而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历。第二种方式倒是省事,却又将容器的内部细节暴露无遗。
正因于此,迭代器模式应运而生,在客户访问类与容器体之间插入一个第三者–迭代器,很好的解决了上述弊端。
1.定义
提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。
2.使用场景
遍历一个容器对象时。
3.简单实现
用书中的例子:小民和小辉分别在公司两个事业部,某天老板安排任务让他们俩统计一下各自部门的员工数据。
Android源码中的模式实现:1.Cursor
当我们使用SQLiteDatabase的query方法查询数据库时,会返回一个Cursor游标对象,该游标的实质就是一个具体的迭代器,我们可以使用它来遍历数据库查询所得的结果集。
5.总结
迭代器模式发展至今,几乎所有的高级语言都有相应的内置实现,对于开发者而言,已经极少会自己去实现迭代器了,所以本章内容更多的是了解而非应用。
1.优点
(1)符合面向对象设计原则中的单一职责原则。
(2)支持对容器对象的多种遍历。弱化了容器类与遍历算法之间的关系。
2.缺点
(1)类文件的增加。
(3)会出现ConcurrentModificationException异常。
(2)遍历过程是一个单向且不可逆的遍历。
>> 14.模板方法模式
模板方法模式是结构最简单的行为型设计模式,也是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。在其结构中只存在父类与子类之间的继承关系。
1.定义
定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
2.模板方法模式中的方法
1.模板方法
一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。所以模板方法大多会定义为final类型,指明主要的逻辑功能在子类中不能被重写。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。由于模板方法是具体方法,因此模板方法模式中的抽象层只能是抽象类,而不是接口。
2.基本方法
(1)抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以
abstract关键字标示。
(2)钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。子类可以通过扩展钩子方法来影响模板方法的逻辑。
3.使用场景
(1)多个子类有公有的方法,并且逻辑基本相同。
(2)重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由各个子类实现。
(3)重构时,模板方法是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子方法约束其行为。
4.简单实现
以电脑开机为例,假设现在有两台电脑,一台Windows系统电脑,一台Mac系统电脑。但是开机流程基本一致:步骤为开启电源、系统检查、加载系统、检查是否需要登录。
》Android源码中的模板方法模式:AsyncTask;Activity的生命周期
1.AsyncTask
在使用AsyncTask时,我们都知道把耗时操作放到doInBackground(Params… params)中,在doInBackground之前,如果想做一些初始化操作,可以把实现写在onPreExecute中,当doInBackground执行完后会执行onPostExecute方法,而我们只需要构建AsyncTask对象,然后执行execute方法。
2.Activity的生命周期
ActivityThread的main函数被调用后,依次执行Activity的onCreate、onStart、onResume函数,用户通常在Activity的子类中覆写onCreate方法,并且在该方法中调用setContentView来设置布局。
6.区别
(1)工厂方法是模板方法的一种特殊版本。
(2)策略模式和模板方法模式都是封装算法,一个用组合,一个用继承。
(3)策略模式和模板模式通常可以互相替换。它们都像试卷,策略模式是选择题,模板模式是填空题。
7.总结
模板方法模式用4个字概括就是:流程封装。也就是把某个固定的流程封装到一个final方法中,并且让子类能够定制这个流程中的某些或者所有步骤,这就要求父类提取公用的代码,提升代码的复用率,同时带来了更好的可扩展性。
1.优点
(1)封装不变部分,扩展可变部分。
(2)提取公共部分代码,便于维护。
2.缺点
需要为每一个基本方法的不同实现提供一个子类,如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大,设计也更加抽象,此时,可结合桥接模式来进行设计。
>> 15.访问者模式
访问者模式是一种行为型模式,它是23种设计模式中最复杂的一个,虽然使用频率不高,但是并不代表可以忽略,在合适的地方,它会带来意想不到的灵活性。访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。
1.定义
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
2.使用场景
(1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
(2)需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
3.使用情景:年终了,公司会给员工进行业绩考核。但是,不同领域的管理人员对于员工的评定标准不一样。现在员工有攻城狮和经理,评定者有CEO和CTO,我们假定CTO只关注攻城狮的代码量、经理的新产品数量,而CEO关注的是攻城狮的KPI和经理的KPI以及新产品数量。
安卓中的著名开源库
ButterKnife、Dagger、Retrofit都是基于APT(Annotation Processing Tools)实现。而编译注解核心依赖APT。当我们通过APT处理注解时,最终会将获取到的元素转换为相应的Element元素,以便获取到它们对应信息。
总结
1.优点
(1)各角色职责分离,符合单一职责原则。
(2)具有优秀的扩展性。
(3)使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。
(4)灵活性。
2.缺点
(1)具体元素对访问者公布细节,违反了迪米特原则。
(2)具体元素变更时导致修改成本大。
(3)违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有依赖抽象。
>> 16.中介者模式
中介者模式也称为调解者模式或调停者模式,是一种行为型模式。
1.定义
中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使它们可以松散耦合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。
2.使用场景
当对象之间的交互操作很多且每个对象的行为操作都依赖彼此时,为防止在修改一个对象的行为时,同时涉及很多其他对象的行为,可使用中介者模式。
4.简单实现
在电脑中,主机部分主要分为:CPU、内存、显卡、IO设备,而将它们整合起来的就是主板,这里主板就是一个中介者。
Android源码中的中介者模式: 1. Keyguard解锁屏
详细机制参考:Android4.0 Keyguard解锁屏机制
总结
其实在Android开发中我们可能无意间就使用了中介者模式,比如登录注册界面,我们使用
EditText的addTextChangedListener监听输入密码的位数、用户名是否为空,密码与确认密码是否一致等等判断时,此时多个控件交互,就是由Activity充当中介者来协调。
1.优点
(1)适当地使用中介者模式可以避免同事类之间的过度耦合,使得各同事类之间可以相对独立地使用。
(2)使用中介者模式可以将对象的行为和协作进行抽象,能够比较灵活的处理对象间的相互作用。
(3)使用中介者模式可以将对象间多对多的关联转变为一对多的关联,使对象间的关系易于理解和维护。
2.缺点
中介者模式是一种比较常用的模式,也是一种比较容易被滥用的模式。对于大多数的情况,同事类之间的关系不会复杂到混乱不堪的网状结构,因此,大多数情况下,将对象间的依赖关系封装的同事类内部就可以的,没有必要非引入中介者模式。滥用中介者模式,只会让事情变的更复杂。所以,我们决定使用中介者模式之前要多方考虑、权衡利弊。
>> 17.代理模式
代理模式也称委托模式,是结构型设计模式之一。是应用广泛的模式之一。
1.定义
为其他对象提供一种代理以控制对这个对象的访问。
2.使用场景
当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
4.简单实现
书中例子:以小民诉讼的流程举例。那么需要代理律师代理,诉讼简单流程:提交申请–>进行举证–>开始辩护–>诉讼完成。
Android源码中的代理模式实现:1.ActivityManagerProxy代理类
ActivityManager是Android中管理和维护Activity的相关信息的类,为了隔离它与ActivityManagerService,有效降低二者的耦合,在这中间使用了ActivityManagerProxy代理类,所有对ActivityManagerService的访问都转换成对代理类的访问,这样ActivityManager就与ActivityManagerService解耦了。
总结
1.优点
(1)对代理者与被代理者进行解耦。
(2)代理对象在客户端和目标对象之间起到一个中介的作用,这样可以起到对目标对象的保护。
2.缺点
基本没有缺点,真要说缺点就是设计模式的通病:对类的增加。
>> 18.组合模式
组合模式也称为部分-整体模式,结构型设计模式之一。
1.定义
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
2.使用场景
(1)表示对象的部分-整体层次结构时。
(2)从一个整体中能够独立出部分模块或功能的场景。
4.简单实现
以文件和文件夹这样的文件系统为例
Android源码中的模式实现: 1.View和ViewGroup的嵌套组合
View和ViewGroup的结构很像上面的UML类图,不过View的视图层级使用的是安全的组合模式。ViewGroup有对View的addView、removeView、getChildAt等方法,想必大家也很熟悉。
总结
1.优点
(1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,他让高层模块忽略了层次的差异,方便对整个层次结构进行控制。
(2)简化了高层模块的代码。
(3)在组合模式中增加新的枝干构件和叶子构件都很方便,无须对现有类库进行修改,符合“开闭原则”。
(4)对树形结构的控制变得简单。
2.缺点
组合模式不容易限制组合中的构件。因为大多数情况下,它们都来自于相同的抽象层,此时,必须进行类型检查来实现,这个实现过程较为复杂。
>> 19.适配器模式
适配器模式是结构型设计模式之一,它在我们的开发中使用率极高,比如ListView、GridView以及RecyclerView都需要使用Adapter。
1.定义
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配无法在一起工作的两个类可以在一起工作。
2.使用场景
(1)系统需要使用现有的类,但此类的接口不符合系统的需要,即接口不兼容。
(2)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
(3)需要一个统一的输出接口,而输入端的类型不可预知。
以笔记本电源适配器为例,电源适配器将220V的电压转换到5V。那么5V电压就是Target接口,220V电压就是Adaptee类,转换就是Adapter。
Android源码中的适配器模式:1.ListView的Adapter
这里ListView的Adapter就是使用的对象适配器模式,Target就是View,Adapter角色就是将Item View输出为View抽象的角色,Adaptee就是需要被处理的Item View。
.总结
1.优点
(1)更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
(2)更好的扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
2.缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
>> 20.装饰模式
装饰模式也称为包装模式,是结构型设计模式之一。
装饰模式是一种用于替代继承技术的一种方案。
1.定义
动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
2.使用场景
(1)需要透明且动态地扩展类的功能时。且在不影响其他对象的情况下。
(2)当不能采用继承对系统进行扩展时可以使用装饰模式。比如final类。
4.简单实现
以一个男孩穿衣装扮为例。实现给男孩在家与出门的穿衣装扮。
区别
1.与代理模式的区别
(1)装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代;而代理模式则是给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用。
(2)装饰模式应该为所装饰的对象增强功能;代理模式是对代理对象施加控制,不对对象本身功能进行增强。
2.与适配器模式的区别
适配器模式是用新接口来调用原接口,原接口对新系统是不可见的;装饰模式增强了其他对象的功能而同时又不改变它的接口。
总结:
1.优点
(1)对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
(2)可以通过一种动态的方式在运行时选择不同的具体装饰类,从而实现不同的行为。
(3)可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
(4)具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
2.缺点
(1)使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
(2)对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
>> 21.享元模式
享元模式是结构型设计模式之一,是对对象池的一种实现。就像它的名字一样,共享对象,避免重复的创建。我们常用的
String 就是使用了共享模式,所以String类型的对象创建后就不可改变,如果当两个String对象所包含的内容相同时,JVM只创建一个String对象对应这两个不同的对象引用。
1.定义
采用一个共享来避免大量拥有相同内容对象的开销。使用享元模式可有效支持大量的细粒度对象。
2.使用场景
(1)系统中存在大量的相似对象。
(2)细粒度的对象都具备较接近的外部状态,而且内部状态与环境不关,也就是说对象没有特定身份。
(3)需要缓冲池的场景。
PS:内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为
享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为
外部状态。
4.简单实现
情景:过年买火车票的时候,我们需要查询车票的情况,那么如果每次查询车票时都创建一个结果,那么必然会大量的创建出许多重复的对象,频繁的去销毁他们,使得GC任务繁重。那么这时我们可以使用享元模式,将这些对象缓存起来,查询时优先使用缓存,没有缓存在重新创建。
总结
1.优点
(1)大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能。
(2)使用享元模式,可以让享元对象可以在不同的环境中被共享。
2.缺点
(1)使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
(2)享元模式将需、享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
>> 22.外观模式
外观模式是结构型设计模式之一,它在开发中的运用频率非常高,是我们
封装API的常用手段。我们经常
使用的三方SDK基本都使用的外观模式,这样可以对用户屏蔽很多实现细节,降低用户使用成本。
1.定义
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。
2.使用场景
(1)为复杂子系统提供一个简单接口,对外隐藏子系统的具体实现、隔离变化。
(2)当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过外观接口进行通信,从而简化了它们之间的依赖关系。
4.简单实例
手机集合了电话功能、短信功能、拍照和GPS等功能。那么以手机为例,简单的用外观模式实现一下。
Android源码中的外观模式:1.Context
Context 是一个抽象类,它的真正实现是ContextImpl 类,通过查看ContextImpl 源码我们可以看到ContextImpl内部封装了很多不同子系统的操作。例如:Activity的跳转、发送广播、启动服务和设置壁纸等,这些工作不是在ContextImpl 中实现,而是交给了具体的子系统进行处理。通过Context 这个抽象类定义了一组接口,ContextImpl实现。这样用户通常情况下就不需要对每个子系统进行了解。这样对用户屏蔽了具体的实现细节,降低了使用成本。
总结
1.优点
(1)对客户程序隐藏子系统的细节,因而减少了客户对于子系统的耦合,能够拥抱变化。
(2)外观类对子系统的接口封装,使得系统更易于使用。
2.缺点
(1)外观类接口膨胀。由于子系统的接口都有外观类统一对外暴露,使得外观类的API接口较多,在一定程度上增加了用户使用成本。
(2)外观类没有遵循开闭原则,当业务出现变更时,可能需要直接修改外观类。
>> 23.桥接模式
桥接模式也称为桥梁模式,是结构型设计模式之一。桥接模式中体现了“单一职责原则”、“开闭原则”、“里氏替换原则”、“依赖倒置原则”等。同时它也是很实用的一种模式。
1.定义
将抽象部分与现实部分分离,使它们都可以独立地进行变化。
2.使用场景
(1)如果一个系统需要在构建的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系。
(2)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,也可以考虑使用桥接模式。
(3)一个类存在两个独立变化的维度,且这两个维度都需要扩展。
4.简单实现
以去咖啡店喝咖啡为例,我们假定咖啡有大杯加糖、大杯不加糖、小杯加糖和小杯不加糖四种。
Android源码中的桥接模式:1.Window与WindowManager
总结
1.优点
(1)分离抽象与现实、灵活的扩展以及对客户来说透明的实现。
(2)桥接模式可以取代多层继承,大大减少了子类的个数。
2.缺点
不容易设计,对开发者来说要有一定的经验要求。理解很容易,设计却不容易。
>>> 24.MVC的介绍与实战
1.MVC的基本介绍
MVC全称是Model - View - Controller,是模型(model)-视图(view)-控制器(controller)的缩写。MVC是一种框架模式而非设计模式,GOF把MVC看作是3种设计模式:观察者模式、策略模式与组合模式的合体,而核心是观察者模式。简而言之,框架是大智慧,用来对软件设计进行分工;设计模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。
1.MVC的优点
(1)首先就是理解比较容易,技术含量不高,这对开发和维护来说成本较低也易于维护与修改。
(2)耦合性不高,表现层与业务层分离各司其职,对开发来说很有利。
2.MVC的缺点
(1)完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难。每个构件在使用之前都需要经过彻底的测试。
(2)对于小项目,MVC反而会带来更大的工作量以及复杂性。
2.MVC在Android中的应用
Android中对MVC的应用很经典,在Android中视图View层一般采用XML文件进行界面的描述。
而对于模型Model部分则大多对应于本地的数据文件或网络获取的数据体,很多情况下我们对这些数据的处理也会在这一层中进行。
最后的控制器Controller则当之无愧的是右Activity承担。
3.总结
虽说上面的介绍中我们感受到Android在MVC方面的结构,但是,这个框架并非我们自己完成的,而是由framework给我们搭建好的并提供给我们,在平时的开发中,特别是用Android开发,我们并不常用到MVC模式去脱离Android UI系统构建自己的框架结构。
>> 25.MVP应用构架模式
1.MVP介绍
MVP模式是MVC模式的一个演化版本,MVP全称Model-View-Presenter。目前MVP在Android应用开发中越来越重要了。
在Android中,业务逻辑和数据存取是紧紧耦合的,很多缺乏经验的开发者很可能会将各种各样的业务逻辑塞进某个Activity、Fragment或者自定义View中,这样会使得这些组件的单个类型臃肿不堪。如果不将具体的业务逻辑抽离出来,当UI变化时,你就需要去原来的View中抽离具体业务逻辑,这必然会很麻烦并且易出错。
2.使用MVP的好处
(1)MVP模式会解除View与Model的耦合,有效的降低View的复杂性。同时又带来了良好的可扩展性、可测试性,保证系统的整洁性和灵活性。
(2)MVP模式可以分离显示层与逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖与具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。
3.MVP模式的三个角色
(1)Presenter – 交互中间人:Presenter主要作为沟通View与Model的桥梁,它从Model层检索数据后,返回给View层,使得View与Model之间没有耦合,也将业务逻辑从View角色上抽离出来。
(2)View – 用户界面:View通常是指Activity、Fragment或者某个View控件,它含有一个Presenter成员变量。通常View需要实现一个逻辑接口,将View上的操作转交给Presenter进行实现,最后,Presenter 调用View逻辑接口将结果返回给View元素。
(3)Model – 数据的存取:Model 角色主要是提供数据的存取功能。Presenter 需要通过Model层存储、获取数据,Model就像一个数据仓库。更直白的说,Model是封装了数据库DAO或者网络获取数据的角色,或者两种数据方式获取的集合。
所以两者的主要区别是,MVP中View不能直接访问Model,需要通过Presenter发出请求,View与Model不能直接通信。
2.与MVVM(Model-View-ViewModel)的区别
MVVM与MVP非常相似,唯一区别是View和Model进行双向绑定,两者之间有一方发生变化则会反应到另一方上。MVVM模式有点像ListView与Adapter、数据集的关系,当数据集发生变化时,调用Adapter的notifyDataSetChanged之后View就直接更新,同时它们之间又没有耦合,使得ListView变得更加灵活。
《Android源码设计模式解析与实战》读书笔记中demo代码:http://download.csdn.net/download/qq_17766199/9411032