7,装饰模式
原文:http://www.cnblogs.com/guoshiandroid/archive/2011/05/21/2053072.html
装饰模式解释:
装饰( Decorator )模式又叫做包装(Wrapper)模式。通过一种对客户端透明的方式来扩展对象的功能,是继承关系的一个替换方案。
英文定义为:Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
装饰模式的UML图:
建造者模式涉及以下的角色:
抽象组件(Component)角色: 一个抽象接口,是被装饰类和装饰类的父接口。
具体组件(Concrete Component)角色:为抽象组件的实现类。
抽象装饰(Decorator)角色:包含一个组件的引用,并定义了与抽象组件一致的接口。
具体装饰(Concrete Decorator)角色:为抽象装饰角色的实现类。负责具体的装饰。
装饰模式的UML图如下所示:
装饰模式深入分析:
装饰模式以一种对客服端透明的方式动态的对对象增加功能,是继承的一种很好的替代方案。
继承是一种面向对象语言特有的而且也是一种非常容易被滥用的复用和扩展的手段。继承关系必须首先符合分类学意义上的基类和子类的感谢,其次继承的子类必须针对基类进行属性或者行为的扩展。继承使得修改或者扩展基类比较容易;但是继承也有很多不足,首先继承破坏了封装,因为继承将基类的实现细节暴露给了子类,其次,如果基类的实现发生了变化,那么子类也就会跟着,这时候我们就不得不改变子类的行为,来适应基类的改变。最后从基类继承而来的实现都是静态的,不可能在运行期(runtime)发生改变,这就使得相应的系统缺乏足够的灵活性。
由于以上诸多的原因,一般尽量是不使用继承来给对象增加功能,此时,装饰模式就是一种更好的选择了,这是因为:首先,装饰模式对客户端而言是透明,客户端根本感觉不到是原始对象还是被装饰过的对象,也就是说装饰模式对客户端而言是透明的;其次装饰者和被装饰对象拥有共同一致的接口,而且装饰者采用对被装饰类的引用的方式使用被装饰对象,这就使得装饰对象可以无限制的动态的装饰被装饰对象;最后装饰对象并不知道被装饰对象是否被装饰过,这就使得面对任何被装饰的对象,装饰者都可以采用一致的方式去处理。
装饰模式的优缺点分析:
优点:
首先:使用装饰模式,能够比使用继承关系更灵活的扩展对象的功能,可以按照业务需求随意的增加对象的功能;其次:通过不同具体的装饰类以及这些具体的装饰类排列组合,可以构造出很多不同种类的装饰结果 缺点:
首先,因为装饰模式有很多装饰类,这就会使系统会是系统可以产生很对对象,产生相应的管理问题;其次,真是由于装饰模式的灵活性,使得随意组合可能会产生很多能够正常编译和运行的对象,但是却不符合逻辑;最后,由于比较的灵活,想对于继承而言就比较的容易出错,而且出错后也不容排查出错误。
装饰模式的实际应用简介:
装饰模式使用以下场合:
想透明并且动态地给对象增加新的职责的时候。
给对象增加的职责,在未来存在增加或减少可能。
用继承扩展功能不太现实的情况下,应该考虑用组合的方式。
在Java IO库的设计和实现中就很好的使用了装饰模式。JDK提供的java.io包中使用了Decorator模式来实现对各种输入输出流的封装。以下将以java.io.OutputStream及其子类为例,讨论一下Decorator模式在IO中的使用。
我们来看一段用来创建IO流的代码:
以下是代码片段:
try {
OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
这段代码对于使用过JAVA输入输出流的人来说再熟悉不过了,我们使用DataOutputStream封装了一个FileOutputStream。这是一个典型的Decorator模式的使用,FileOutputStream相当于Component,DataOutputStream就是一个Decorator。
在java.io包中,不仅OutputStream用到了Decorator设计模式,InputStream,Reader,Writer等都用到了此模式。而作为一个灵活的,可扩展的类库,JDK中使用了大量的设计模式,比如在Swing包中的MVC模式,RMI中的Proxy模式等等。对于JDK中模式的研究不仅能加深对于模式的理解,而且还有利于更透彻的了解类库的结构和组成。
8,外观模式
原文:http://www.cnblogs.com/guoshiandroid/archive/2011/05/28/2060943.html
外观模式(Façade Pattern)是比较常用的一种软件设计模式,属于结构型模式的一种。它为一组具有类似功能的类群,比如类库,子系统等等提供一个统一的高层接口。这个高层接口以一种简单一致的界面展现给使用者,从而使得子系统、类群等更容易使用。
英文定义为:Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
建造者模式涉及以下的角色:
外观(Facade)角色: 为调用方定义简单的调用接口。
客户端(Clients)角色:通过Facade接口调用提供某功能的内部类群,读写子系统各个接口的数据资源。
类群(Packages)角色:功能提供者。指提供功能的类群(模块或子系统)。
外观模式的UML图如下所示:
更具体的UML图如下所示:
外观模式深入分析:
在真实的应用系统中,一个子系统可能由很多类组成。子系统的客户端为了它们的需要,需要和子系统中的一些类进行交互。客户端和子系统的类进行直接的交互会导致客户端对象和子系统之间高度耦合。任何的类似于对子系统中类的接口的修改,会对依赖于它的所有的客户类造成影响。外观模式(Facade pattern)为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。
外观是一个能为子系统和客户端提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度。这样,就可以使得当子系统改变的时候,客户端可以保持不变性。
尽管客户端使用由外观提供的简单接口,但是当需要的时候,客户端还是可以视外观不存在,直接访问子系统中的低层次的接口。这种情况下,它们之间的依赖/耦合度和原来一样。
外观模式的优缺点分析:
优点:
外观模式通过提供一个统一的对外接口,一方面可以避免外部系统和子系统之间的直接联系,从而降低系统间的依赖程度;另外一方面,如何外部系统想和子系统进行直接的交互,也可以绕过外观模式,这使得外部系统对子系统的使用非常的灵活。
缺点:
外观模式对外部系统提供的接口是有限的,从这个角度上讲,是限制了外部系统对子系统调用的灵活性。
外观模式的实际应用简介:
一般而言,外观模式使用于以下场合:
第一:为一个复杂的子系统提供一个简单的接口。子系统往往因为不断的演化而变的越来越复杂,使用外观模式可以保持外部系统对子系统调用的简洁性,而那些需要细节调用的用户却可以越过外观模式直接对子系统进行调用。
第二:引进外观模式可以将一个子系统和使用它的客户端以及其它的子系统分离开来,这就提高了子系统的独立性和可移植性。
第三:在构建一个层次化结构的时候,可以使用外观模式定义每一个层次对外交互的接口。此时,层与层之间只需要通过外观进行通信,从而简化层与层之间的依赖关系。
外观模式是一种得到广泛应用的模式,例如我们熟知的MVC模式就采用了外观模式。在MVC,在MVC架构模式中,每一层并不需要知道其它层次的细节,只是通过层与层之间的接口调用即可。这大大的方便了开发。
9,享元模式 flyweight pattern
原文:http://www.cnblogs.com/guoshiandroid/archive/2011/05/27/2060244.html
扩展:http://www.cnblogs.com/rush/archive/2011/10/01/2197785.html
http://alaric.iteye.com/blog/1914498
享元模式解释:
享元模式(Flyweight Pattern)是通过使用共享的方式,达到高效地支持大量的细粒度对象。它的目的就是节省占用的空间资源,从而实现系统性能的改善。
享元的英文是Flyweight,它是一个来自于体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量的级别。把这个单词移植到软件工程里面,也是用来表示特别小的对象,即细粒度对象。至于为什么我们把Flyweight翻译为“享元”,可以理解为共享元对象,也就是共享细粒度对象。
英文定义为:Use sharing to support large numbers of fine-grained objects efficiently.
享元模式涉及以下的角色:
抽象享元(Flyweight)角色: 它是所有具体享元类的超类。为这些类规定出需要实现的公共接口,那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
具体享元(ConcreteFlyweight)角色:具体享元类实现了抽象享元类所规定的接口。如果有内蕴状态(Internal State)的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元类又称为单纯具体享元类,因为复合享元类是由单纯具体享元角色通过复合而成的。
不能共享的具体享元类(UnsharableFlyweight): 不能共享的享元类,又叫做复合享元类。一个复合享元对象是由多个单享元对象组成,这些组成的对象是可以共享的,但是复合享元类本身并不能共享。
享元工厂类(FlyweightFactory): 享元工厂类负责创建和管理享元对象。当一个客户端对象请求一个享元对象的时候,享元工厂需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
客户类(Client): 客户类需要自行存储所有享元对象的外蕴状态。
享元模式的UML图如下所示:
享元模式深入分析:
它通过与其他类似对象共享数据来减小内存占用。
享元对象的第一类状态称为内蕴状态(Internal State)。它不会随环境改变而改变,存储在享元对象内部,因此内蕴状态是可以共享的,对于任何一个享元对象来讲,它的值是完全相同的。
享元对象的第二类状态称为外蕴状态(External State)。它会随环境的改变而改变,因此是不可以共享的状态,对于不同的享元对象来讲,它的值可能是不同的。享元对象的外蕴状态必须由客户端保存,在享元对象被创建之后,需要使用的时候再传入到享元对象内部。所以享元的外蕴状态与内蕴状态是两类相互独立的状态,彼此没有关联。
享元模式的优缺点分析:
优点:
使用享元模式可以降低内存中对象的数量,从而为系统节省大量的内存空间。
缺点:
享元模式使得系统更加复杂,因为为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。而且,由于享元工厂需要维护所有的享元对象,此时,如果要维护的享元对象很多的话,在查找具体的享元对象的时候就要消耗大量的时间,换句话说,享元模式是一种以时间换空间的模式。
享元模式的实际应用简介:
享元模式在一般的项目开发中并不常用,而是常常应用于系统底层的开发,以便解决系统的性能问题。
Java中的String类型就是使用了享元模式。
如果在Java中已经创建了一个字符串对象string1,那么下次再创建相同的字符串string2的时候,系统只是把string2的引用指向string1所引用的具体对象,这就实现了相同字符串在内存中的共享。如果每次执行string1=“abc”操作的时候,都创建一个新的字符串对象的话,那么内存的开销会很大。
如果大家有兴趣的话,可以用下面的程序进行测试,就会知道string1和string2的引用是否一致:
String string1= "爱你一万年,爱你的心永不改变";
String string2= "爱你一万年,爱你的心永不改变";
//“==”用来判断两个对象是否是同一个,equals判断字符串的值是否相等
if( string1 == string2 ){
System.out.println("两者一样");
}else{
System.out.println("两者不一样");
}
程序运行后,输出的结果为“两者一样”,这说明String类的设计采用了享元模式。如果string1的内容发生了变化,比如执行了string1 += "让我们结婚吧!"的语句,那么s1与s2的引用将不再一致。
我们额外的谈一下PHP中String的处理。作为一种弱类型语言,PHP的字符串类型是一种基本类型,不是对象。另外,它的执行方式与Java有明显区别,每一个脚本文件执行开始,将会装入所有需要的资源;执行结束后,又将占用的资源就立即全部释放,所以它基本上不会产生类似的性能问题,它的字符串处理的设计,自然也使用不到享元模式。
温馨提示:
面向对象虽然很好地解决了抽象性的问题,但是对于一个实际运行的软件系统,我们还需要考虑面向对象的代价问题,享元模式解决的就是面向对象的代价问题。享元模式采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。
在具体实现方面,我们要注意对象状态的处理,一定要正确地区分对象的内蕴状态和外蕴状态,这是实现享元模式的关键所在。
享元模式的优点在于它大幅度地降低内存中对象的数量。为了做到这一点,享元模式也付出了一定的代价:
1、享元模式为了使对象可以共享,它需要将部分状态外部化,这使得系统的逻辑变得复杂。
2、享元模式将享元对象的部分状态外部化,而读取外部状态使得运行时间会有所加长。
另外,还有一个比较令人关心的问题:到底系统需要满足什么样的条件才能使用享元模式。对于这个问题,总结出以下几点:
1、一个系统中存在着大量的细粒度对象;
2、这些细粒度对象耗费了大量的内存。
3、这些细粒度对象的状态中的大部分都可以外部化;
4、这些细粒度对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。
5、软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
满足以上的这些条件的系统可以使用享元对象。最后,使用享元模式需要维护一个记录了系统已有的所有享元的哈希表,也称之为对象池,而这也需要耗费一定的资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。