首页 > 代码库 > Android 设计模式

Android 设计模式

简单介绍

项目开发中发现问题、解决这个问题这个过程中会出现非常多问题,比方反复出现、某个问题的遗留,这些问题的本质就是设计模式。

今天记录设计模式的知识点。


内容

在java以及其它的面向对象设计模式中,类与类之间主要有6种关系。他们各自是:依赖、关联、聚合、组合、继承、实现。

它们的耦合度依次增强。

依赖关系:
对于两个相对独立的对象,当一个对象负责构造还有一个对象的实例,或者依赖还有一个对象的服务时,这两个对象之间主要体现为依赖关系。
关联关系:
分为单向关联和双向关联。在java中。单向关联表现为:类A其中使用了类B。其中类B是作为类A的成员变量。双向关联表现为:类A其中使用了类B作为成员变量;同一时候类B中也使用了类A作为成员变量。


聚合关系:
是关联关系的一种,耦合度强于关联,他们的代码表现是同样的,仅仅是在语义上有所差别:关联关系的对象间是相互独立的,而聚合关系的对象之间存在着包容关系。他们之间是“总体-个体”的相互关系。


组合关系:
是一种耦合度更强的关联关系。存在组合关系的类表示“总体-部分”的关联关系。“总体”负责“部分”的生命周期。他们之间是共生共死的。并且“部分”单独存在时没有不论什么意义。
继承:
表示类与类(或者接口与接口)之间的父子关系。


实现:
表示一个类实现一个或多个接口的方法。



设计原则

要点 定义 描写叙述
单一职责原则不要存在多于一个导致类变更的原因。通俗的说,即一个类仅仅负责一项职责。

问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而须要改动类T时,有可能会导致原本执行正常的职责P2功能发生问题。



解决方式:遵循单一职责原则。分别建立两个类T1、T2,使T1完毕职责P1功能,T2完毕职责P2功能。这样。当改动类T1时,不会使职责P2发生问题风险;同理,当改动T2时,也不会使职责P1发生问题风险。

里氏替换原则定义1:假设对每个类型为 T1的对象 o1。都有类型为 T2 的对象o2,使得以 T1定义的全部程序 P 在全部的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化。那么类型 T2 是类型 T1 的子类型。
定义2:全部引用基类的地方必须能透明地使用其子类的对象。
问题由来:有一功能P1,由类A完毕。现须要将功能P1进行扩展。扩展后的功能为P,其中P由原有功能P1与新功能P2组成。

新功能P由类A的子类B来完毕,则子类B在完毕新功能P2的同一时候。有可能会导致原有功能P1发生问题。

解决方式:当使用继承时,遵循里氏替换原则。

类B继承类A时,除加入新的方法完毕新增功能P2外。尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

依赖倒置原则高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节;细节应该依赖抽象。问题由来:类A直接依赖类B。假如要将类A改为依赖类C,则必须通过改动类A的代码来达成。

这样的场景下,类A通常是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作。假如改动类A,会给程序带来不必要的风险。



解决方式:将类A改动为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大减少改动类A的几率。

接口隔离原则client不应该依赖它不须要的接口;一个类对还有一个类的依赖应该建立在最小的接口上。问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,假设接口I对于类A和类B来说不是最小接口。则类B和类D必须去实现他们不须要的方法。



解决方式:将臃肿的接口I拆分为独立的几个接口。类A和类C分别与他们须要的接口建立依赖关系。也就是採用接口隔离原则。

迪米特法则一个对象应该对其它对象保持最少的了解。问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对还有一个类的影响也越大。



解决方式:尽量减少类与类之间的耦合。

开闭原则一个软件实体如类、模块和函数应该对扩展开放。对改动关闭。

问题由来:在软件的生命周期内,由于变化、升级和维护等原因须要对软件原有代码进行改动时。可能会给旧代码中引入错误,也可能会使我们不得不正确整个功能进行重构,并且须要原有代码经过又一次測试。

解决方式:当软件须要变化时,尽量通过扩展软件实体的行为来实现变化。而不是通过改动已有的代码来实现变化。
   

设计模式

要点 定义 描写叙述
单例模式确保一个类仅仅有一个实例,并且自行实例化并向整个系统提供这个实例。单例模式注意事项:
仅仅能使用单例类提供的方法得到单例对象。不要使用反射,否则将会实例化一个新对象。不要做断开单例类对象与类中静态引用的危急操作。多线程使用单例使用共享资源时,注意线程安全问题。
工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。在工厂方法模式中,核心的工厂类不再负责全部的对象的创建。而是将详细创建的工作交给子类去做。

这个核心类则摇身一变,成为了一个抽象工厂角色,仅负责给出详细工厂子类必须实现的接口。而不接触哪一个类应当被实例化这样的细节。

抽象工厂模式为创建一组相关或相互依赖的对象提供一个接口,并且无需指定他们的详细类。在下面情况下,适用于工厂方法模式:
(1) 当一个类不知道它所必须创建的对象的类的时候。
(2) 当一个类希望由它的子类来指定它所创建的对象的时候。


(3) 当类将创建对象的职责托付给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

模版方法模式定义一个操作中算法的框架,而将一些步骤延迟到子类中。使得子类能够不改变算法的结构就可以重定义该算法中的某些特定步骤。子类能够置换掉父类的可变部分。可是子类却不能够改变模板方法所代表的顶级逻辑。
  每当定义一个新的子类时,不要依照控制流程的思路去想,而应当依照“责任”的思路去想。换言之,应当考虑哪些操作是必须置换掉的,哪些操作是能够置换掉的。以及哪些操作是不能够置换掉的。使用模板模式能够使这些责任变得清晰。

建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程能够创建不同的表示。与抽象工厂的差别:在建造者模式里,有个指导者,由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。

即建造模式能够强制实行一种分步骤进行的建造过程。


  建造模式是将复杂的内部创建封装在内部。对于外部调用的人来说,仅仅须要传入建造者和建造工具。对于内部是怎样建造成成品的,调用者无需关心。
在Java的应用中JavaMail使用到了该模式。

代理模式为其它对象提供一种代理以控制对这个对象的訪问。所谓代理,就是一个人或者机构代表还有一个人或者机构採取行动。

在一些情况下。一个客户不想或者不能够直接引用一个对象。而代理对象能够在client和目标对象之间起到中介的作用。

原型模式用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

原型模式要求对象实现一个能够“克隆”自身的接口,这样就能够通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再须要关心这个实例本身的类型。仅仅要实现了克隆自身的方法,就能够通过这种方法来获取新的对象,而无须再去通过new来创建。
在Java语言里深度克隆一个对象。经常能够先使对象实现Serializable接口,然后把对象(实际上仅仅是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便能够重建对象。

原型模式的长处
  原型模式同意在执行时动态改变详细的实现类型。

原型模式能够在执行期间,由客户来注冊符合原型接口的实现类型,也能够动态地改变详细的实现类型。看起来接口没有不论什么变化,但事实上执行的已经是另外一个类实例了。

由于克隆一个原型就相似于实例化一个类。



原型模式的缺点
  原型模式最基本的缺点是每个类都必须配备一个克隆方法。配备克隆方法须要对类的功能进行通盘考虑,这对于全新的类来说不是非常难。而对于已经有的类不一定非常easy,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候。

中介者模式用一个中介者对象封装一系列的对象交互。中介者使各对象不须要显示地相互作用。从而使耦合松散,并且能够独立地改变它们之间的交互。

中介者模式的长处
适当地使用中介者模式能够避免同事类之间的过度耦合,使得各同事类之间能够相对独立地使用。
使用中介者模式能够将对象间一对多的关联转变为一对一的关联。使对象间的关系易于理解和维护。
使用中介者模式能够将对象的行为和协作进行抽象,能够比較灵活的处理对象间的相互作用。

适用场景
       在面向对象编程中,一个类必定会与其它的类发生依赖关系。全然独立的类是没有意义的。

一个类同一时候依赖多个类的情况也相当普遍,既然存在这样的情况,说明,一对多的依赖关系有它的合理性。适当的使用中介者模式能够使原本凌乱的对象关系清晰,可是假设滥用,则可能会带来反的效果。

一般来说,仅仅有对于那种同事类之间是网状结构的关系,才会考虑使用中介者模式。能够将网状结构变为星状结构,使同事类之间的关系变的清晰一些。
       中介者模式是一种比較经常使用的模式,也是一种比較easy被滥用的模式。对于大多数的情况,同事类之间的关系不会复杂到混乱不堪的网状结构。因此,大多数情况下,将对象间的依赖关系封装的同事类内部就能够的。没有必要非引入中介者模式。滥用中介者模式,仅仅会让事情变的更复杂。

命令模式意图:将一个请求封装为一个对象,从而可用不同的请求对客户进行參数化。对请求排队或记录日志。以及支持可撤销的操作
动机:将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来。
常见应用:
1、工作队列,线程池。日程安排
2、日志请求(系统恢复)
要点:
1、命令模式将发出请求的对象和执行请求的对象解耦
2、在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作
3、调用者通过调用命令对象的execute()发出请求。这会使得接收者的动作被调用
4、调用者能够接受命令当作參数,甚至在执行时动态的进行
5、命令能够支持撤销。做法是实现一个undo()方法来回到execute()被执行前的状态
6、宏命令是命令的一种简单的延伸,同意调用多个命令。宏方法也能够支持撤销
7、实际操作时。非经常见使用"聪明"命令对象,也就是直接实现了请求,而不是将工作托付给接受者(弊端?)
8、命令也能够用来实现日志和事物系统
责任链模式使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

一个纯的责任链模式要求一个详细的处理者对象仅仅能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不同意出现某一个详细处理者对象在承担了一部分责任后又 把责任向下传的情况。


  在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收。在一个不纯的责任链模式里面,一个请求能够终于不被不论什么接收端对象所接收。
  纯的责任链模式的实际样例非常难找到,一般看到的样例均是不纯的责任链模式的实现。

有些人觉得不纯的责任链根本不是责任链模式。这或许是有道理的。可是在实际的系统里,纯的责任链非常难找到。假设坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大意义了。

装饰模式又名包装(Wrapper)模式,装饰模式以对client透明的方式扩展对象的功能。是继承关系的一个替代方案。装饰模式与类继承的差别:
1)    装饰模式是一种动态行为。对已经存在类进行任意组合。而类的继承是一种静态的行为,一个类定义成什么样的,该类的对象便具有什么样的功能,无法动态的改变。


2)    装饰模式扩展的是对象的功能,不须要添加类的数量,而类继承扩展是类的功能,在继承的关系中,假设我们想添加一个对象的功能,我们仅仅能通过继承关系,在子类中添加两个方法。
3)    装饰与继承比較图:
4)    装饰模式是在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能,它是通过创建一个包装对象,也就是装饰来包裹真是的对象。


5.    装饰模式把对client的调用委派给被装饰的类。装饰模式的关键在于这样的扩展全然透明的。

策略模式定义一组算法。将每个算法都封装起来,并且使他们之间能够互换。


策略模式的长处在于你能够动态的改变对象的行为。

    策略模式属于对象行为型模式。主要针对一组算法,将每个算法封装到具有共同接口的独立的类中,从而使得它们能够相互替换。策略模式使得算法能够在不影响 到client的情况下发生变化。通常。策略模式适用于当一个应用程序须要实现一种特定的服务或者功能。并且该程序有多种实现方式时使用。
适配器模式基于现有类所提供的服务,向客户提供接口,以满足客户的期望。



适配器模式的用意是要改变源的接口,以便于目标接口相容。缺省适配的用意稍有不同。它是为了方便建立一个不平凡的适配器类而提供的一种平凡实现。

适配器模式的长处
  更好的复用性
  系统须要使用现有的类,而此类的接口不符合系统的须要。那么通过适配器模式就能够让这些功能得到更好的复用。
  更好的扩展性
  在实现适配器功能的时候。能够调用自己开发的功能,从而自然地扩展系统的功能。


适配器模式的缺点
  过多的使用适配器,会让系统非常零乱,不易总体进行把握。比方,明明看到调用的是A接口。事实上内部被适配成了B接口的实现,一个系统假设太多出现这样的情况。无异于一场灾难。

因此假设不是非常有必要,能够不使用适配器,而是直接对系统进行重构。

迭代器模式提供一种方法訪问一个容器对象中各个元素,而又不暴露该对象的内部细节。在jdk中,与迭代器相关的接口有两个:Iterator 与 Iterable 
Iterator:迭代器。Iterator及其子类通常是迭代器本身的结构与方法; 
Iterable:可迭代的,那些想用到迭代器功能的其它类,如AbstractList HashMap等。须要实现该接口。

 

组合模式将对象组合成树形结构以表示‘部分-总体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

对象通过实现(继承)统一的接口(抽象类),调用者对单一对象和组合对象的操作具有一致性。
通过实现组合模式,调用者对组合对象的操作与对单一对象的操作具有一致性。

调用者不用关心这是组合对象还是文件,也不用关心组合对象内部的详细结构,就能够调用相关方法,实现功能。

观察者模式定义对象间一种一对多的依赖关系,使得当每个对象改变状态,则全部依赖于它的对象都会得到通知并自己主动更新。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同一时候监听某一个主题对象。这个主题对象在状态上发生变化时,会通知全部观察者对象,使它们能够自己主动更新自己。

在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。

门面模式外部与一个子系统的通信必须通过一个统一的门面对象进行。

门面模式的长处:
  ●  松散耦合
  门面模式松散了client与子系统的耦合关系。让子系统内部的模块能更easy扩展和维护。
  ●  简单易用
  门面模式让子系统更加易用,client不再须要了解子系统内部的实现,也不须要跟众多子系统内部的模块进行交互,仅仅须要跟门面类交互就能够了。
  ●  更好的划分訪问层次
  通过合理使用Facade。能够帮助我们更好地划分訪问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把须要暴露给外部的功能集中到门面中。这样既方便client使用,也非常好地隐藏了内部的细节。
备忘录模式在不破坏封装性的前提下。捕获一个对象的内部状态。并在该对象之外保存这个状态。这样就能够将该对象恢复到原先保存的状态。备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。

备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而能够在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式经常与命令模式和迭代子模式一同使用。

訪问者模式封装某些作用于某种数据结构中各元素的操作。它能够在不改变数据结构的前提下定义作用于这些元素的新的操作。
訪问者模式是对象的行为模式。訪问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作须要改动的话,接受这个操作的数据结构则能够保持不变。
訪问者模式的长处
  好的扩展性
  能够在不改动对象结构中的元素的情况下,为对象结构中的元素加入新的功能。
  好的复用性
  能够通过訪问者来定义整个对象结构通用的功能。从而提高复用程度。
  分离无关行为
  能够通过訪问者来分离无关的行为。把相关的行为封装在一起,构成一个訪问者。这样每个訪问者的功能都比較单一。

訪问者模式的缺点
  对象结构变化非常困难
  不适用于对象结构中的类经常变化的情况。由于对象结构发生了改变,訪问者的接口和訪问者的实现都要发生对应的改变,代价太高。


  破坏封装
  訪问者模式通常须要对象结构开放内部数据给訪问者和ObjectStructrue。这破坏了对象的封装性。

状态模式当一个对象的内在状态改变时同意改变其行为。这个对象看起来像是改变了其类。
状态模式同意一个对象在其内部状态改变的时候改变其行为。

这个对象看上去就像是改变了它的类一样。

 
解释器模式给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。

 
享元模式复用我们内存中已存在的对象。减少系统创建对象实例的性能消耗。

Flyweight在拳击比赛中指最轻量级。即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是由于这样更能反映模式的用意。享元模式是对象的结构模式。享元模式以共享的方式高效地支持大量的细粒度对象。
享元模式採用一个共享来避免大量拥有同样内容对象的开销。这样的开销最常见、最直观的就是内存的损耗。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。

桥梁模式将抽象和实现解耦,使得两者能够独立地变化。
桥梁模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者能够独立地变化”。

桥梁模式在Java应用中的一个非常典型的样例就是JDBC驱动器。JDBC为全部的关系型数据库提供一个通用的界面。一个应用系统动态地选择一个合适的驱动器,然后通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。

   

项目

写了一个Android的项目体现23中设计模式,项目如图:

技术分享

技术分享


測试代码:

public void onClickSingleMode(View view) { // 单例
		SingleMode.getInstance();
	}

	public void onClickFactoryMethodModel(View view) {// 工厂方法
		IProduct iProduct = new FactoryMethodModel();
		iProduct.productMethod();
		iProduct = new Tree();
		iProduct.productMethod();
	}

	public void onClickAbstractFactoryModel(View view) {// 抽象工厂
		AbstractFactoryModel.test();
	}

	public void onClickTemplateMethodModel(View view) {// 模版方法模式
		TemplateMethodModel.test();
	}

	public void onClickBuilderMode(View view) {// 建造者模式
		BuilderMode.test();
	}

	public void onClickProxyMode(View view) {// 代理模式
		ProxyMode.test();
	}

	public void onClickCloneMode(View view) {// 原型模式
		CloneMode.test();
	}

	public void onClickIntermediaryModel(View view) {// 中介者模式
		IntermediaryModel.test1();
		IntermediaryModel.test2();
	}

	public void onClickCommandMode(View view) {// 命令模式
		CommandMode.test();
	}

	public void onChainOfResponsibilityModel(View view) {// 责任链模式
		ChainOfResponsibilityModel.test();
	}

	public void onClickDecorativeMode(View view) {// 装饰模式
		DecorativeMode.test();
	}

	public void onClickStrategyMode(View view) {// 策略模式
		StrategyMode.test();
	}

	public void onClickIteratorModel(View view) {// 模式
		IteratorModel.test();
	}

	public void onClickCombinationMode(View view) {// 组合模式
		CombinationMode.test();
	}

	public void onClickObserverMode(View view) {// 观察者模式
		ObserverMode.test();
	}

	public void onClickWindowMode(View view) {// 门面模式
		WindowMode.test();
	}

	public void onClickMemoMode(View view) {// 备忘录模式
		MemoMode.test();
	}

	public void onClickVisitorMode(View view) {// 訪问者模式
		VisitorMode.test();
	}

	public void onClickStateModel(View view) {// 状态模式
		StateModel.test();
	}

	public void onClickParserMode(View view) {// 解释器模式
		ParserMode.test();
	}

	public void onClickFlyweightMode(View view) {// 享元模式
		FlyweightMode.test();
	}

	public void onClickBridgeMode(View view) {// 桥梁模式
		BridgeMode.test();
	}


总结

假设设计模式在编码设计生涯中用得极少。主要原因是对设计模式的理解还不够,认识不到问题的存在。


由于不能正确的分析问题、认识问题,当然也不可能非常好的解决这个问题了。


项目下载

Android 设计模式