第一篇:Java23种设计模式6大原则总结
设计模式概念:一套被反复使用、多数人知晓、经过分类编目的优秀代码设计经验的总结。设计模式要素:模式名称、问题、举例、末态环境、推理、其他有关模式、已知的应用。设计模式分类:创建型、结构型、行为型。
创建型模式功能:1.统所使用的具体类的信息封装起来;
2.类的实例是如何被创建和组织的。
创建型模式作用:1.封装创建逻辑,不仅仅是new一个对象那么简单。
2.封装创建逻辑变化,客户代码尽量不修改,或尽量少修改。
常见的创建型模式:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。常见的结构型模式:代理模式、装饰模式、适配器模式、组合模式、桥梁模式、外观模式、享元模式。
常见行为型模式:模板方法模式、命令模式、责任链模式、策略模式、迭代器模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式、解释器模式。
单一职责原则:一个类应该只有一个职责。
优点:降低类的复杂性;提高类的可读性;提高代码的可维护性和复用性;降低因变更引起的风险。
里氏替换原则:
优点:代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;提高代码的可重用性;提高代码的可扩展性;提高产品或项目的开放性。
缺点:1.继承是入侵式的。只要继承,就必须拥有父类所有属性和方法。
2.降低代码的灵活性。子类必须拥有父类的属性和方法,使子类收到限制。
3.增强了耦合性。当父类的常量、变量和方法修改时,必须考虑子类的修改,这种修改可能造成大片的代码需要重构。
依赖倒置原则:高层模块不应该依赖低层模块,两者都依赖其抽象;抽象不依赖细节;细节应该依赖于抽象。
在Java中的表现:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;接口或抽象类不依赖于是实现类;实现类依赖于接口或抽象类。
接口隔离原则:1.一个类对另外一个类的依赖性应当是建立在最小的接口上的 2.一个接口代表一个角色,不应当将不同的角色交给一个接口。3.不应该强迫客户使用它们的不同方法。
如图所示的电子商务系统在三个地方会使用到订单类:一个是门户,只能有查询方法;一个是外部系统,有添加订单的方法;一个是管理后台,添加、删除、修改、查询都要用到。“原子”在实践中的衡量规则:
1.一个接口只对一个子模块或者业务逻辑进行分类。2.只保留接口中业务逻辑需要的public方法。
3.尽量修改污染了的接口,若修改的风险较大,则可采用适配器模式进行转化处理。4.接口设计应因项目而异,因环境而异,不能照搬教条。迪米特法则:(表述)只与你直接的朋友们通信;不要跟“陌生人”说话;每一个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位。
对迪米特法则进行模式设计有两个:外观模式、中介者模式。开闭原则:一个软件实体应当对扩展开放,对修改关闭。
重要性体现:提高复用性;提高维护性;提高灵活性;易于测试 单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。
优点:1.在内存中只有一个实例,减少了内存的开支。2.只生成一个实例,减少了系统的性能开销。3.避免对资源的多重占用。
4.可以在系统设置全局的访问点,优化和共享资源访问。缺点:1.无法创建子类,扩展困难,若要扩展,除修改代码基本上没有第二种途径可以实现。2.对测试不利。3.与单一职责原则有冲突。饿汉式单例类与懒汉式单例类之间的区别:
1.饿汉式单例类在被加载时实例化,而懒汉式单例类在第一次引用时实例化。2.从资源利用效率上说,饿汉式单例类要差一点,但从速度和反应时间的角度来讲,饿汉式单例类则比懒汉式单例类稍好些。
3.饿汉式单例类可以在Java中实现,但不易在C++中实现。单例模式的使用场景:
1.要求生成唯一的序列号环境。
2.在整个项目中需要一个共享访问点或共享数据。3.创建一个对象需要消耗的资源过多。4.需要定义大量的静态方法的环境。使用单例模式的注意事项:
1.在使用任何EJB、RMI和JINI的分布式系统中,应当避免使用有状态的单例类。2.同一个JVM中会有多个类加载器,当两个类加载器同时加载一个类时,会出现两个实例,此时也应当尽量避免使用有状态的单例类。
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化那个类。
优点:良好的封装性,代码结构清晰;优秀的可扩展性;屏蔽产品类;典型的解耦框架。使用场景:1.工厂方法模式是new一个对象的替代品,故在所有需要生成对象的地方都可以使用,但是需要慎重考虑是否需要增加一个工厂类进行管理,增加代码的复杂度。
2.需要灵活的、可扩展的框架时。3.可以用在异构项目中。
4.可以使用在测试驱动开发的框架下。
抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,且无需指定它们的具体类。优点: 1.产品族内的约束为非公开状态,在不同的工厂中,各种产品可能具有不同的相互依赖关系,这些依赖关系由工厂封装在其内部,对于工厂的使用者是不可见的。
2.生产线的扩展非常容易,如果要针对同一产品族建立新的生产线,只需要实现产品族中的所有产品接口并建立新的工厂类即可。
缺点: 产品族本身的扩展非常困难,如果需要在产品族中增加一个新的产品类型,则需要修改多个接口,并且会影响已有的工厂类。
使用场景:当一个对象族(或是一组没有任何关系的对象)都有相同的约束。
建造者将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。优点:1.封装性,可以使客户端不必知道产品内部组成的细节。2.建造者独立,容易扩
3.便于控制细节风险。
使用场景:1.相同的方法,不同的执行顺序,产生不同的结果。2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。3.产品类非常复杂,或者产品类中的方法调用顺序不同产生了不同的效能。4.在对象创建过程中会使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到时。原型模式:用原型实例制定创建对象的种类,并且通过复制这些原型创建新的对象。优点:性能优良;逃避构造函数的约束。
使用场景:资源优化场景;性能和安全要求场景;一个对象多个修改者的场景。结构型模式:为其他对象提供一种代理以控制对这个对象的访问。
种类:远程代理、虚拟代理、保护代理、缓存代理、同步代理、智能引用代理
优点:1.职责清晰:真实的角色实现实际的业务逻辑,不用关心其他非本职的事务,通过后期的代理完成附加的事务,附带的结果就是编程简洁清晰。
2.高扩展性:具体主题角色随需求不同可能有很多种,但只要实现了接口,代理类就完全可以在不做任何修改的情况下代理各种真实主题角色。
3.智能化:代理类可以在运行时才确定要去代理的真实主题,这是一种强大的功能。使用场景:代理模式的应用非常广泛,大到一个系统框架、企业平台,小到事务处理、代码片段,随处可见代理模式的使用,例如,JavaRMI的远程调用和AOP。
装饰模式:动态的给一个对象添加一些额外的职责。
优点:装饰类和被装饰类都可以独立发展,而不会相互耦合;装饰模式是继承关系的一个替代方案;装饰模式可以动态地扩展一个实现类的功能。
使用场景:1.需要扩展一个类的功能,或给一个类增加附加功能。
2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。3.需要为一批类进行改装或加装功能。适配器模式:将一个类的接口变换成客户端所期待的的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
优点:可以让两个没有任何关系的类在一起运行;增加了类的透明度;提高类的复用度;增强代码的灵活性。
使用场景:修改一个已经投产中的系统时,需要对系统进行扩展。此时使用一个已有类,但这个类不符合系统中的接口,这是使用适配器模式是最合适的,它可以将不符合系统接口的类进行转换,转换成符合系统接口的、可以使用的类。
组合模式:将组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
优点:高层模块调用简单;节点自由增加。
缺点:不易控制树枝构件的类型;不易使用继承的方法来增加新的行为。
使用场景:1.需要描述对象的部分和整体的等级结构,如树形菜单、文件和文件夹管理。2.需要客户端忽略个体构件和组合构件的区别,平等对待所有的构件。桥梁模式:将抽象和现实解耦,使得两者可以独立地变化。优点:1.抽象和现实的分离是桥梁模式的主要特点,是为了解决继承的缺点而提出的设计模式。在该模式下,实现可以不受抽象的约束,不用绑定在一个固定的抽象层次上。2.实现对客户的透明,客户端不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。使用场合:1.如果一个系统需要在构件的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
2.设计要求实现化角色的任何改变不应当影响客户端,或者说实现化及角色的改变对客户端是完全透明的。
3.一个构件有多于一个抽象化角色和实现化角色,系统需要它们之间进行动态耦合。4.不希望或不适合使用继承的场合。
外观模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。
优点:1.减少系统的相互依赖,所有的依赖都是对Façade对象的依赖,与子系统无关。
2.提高灵活性,不管子系统系统内部如何变化,只要不影响Façade对象,任何活动都是自由的。
3.提高安全性,Façade中未提供的方法,外界就无法访问,提高系统的安全性。使用场景:1.为一个复杂的模块或子系统提供一个供外界访问的接口。
2.子系统相对独立,外界子系统的访问只要黑箱操作即可。3.预防风险扩散,使用Façade进行访问操作控制。
享元模式:使用共享对象可有效地支持大量的细粒度的对象。
优点:大幅减少内存中对象的数量,降低程序内存的占用,提高性能。缺点:1.增加了系统的复杂性,需要分出外部状态和内部状态,而且内部状态具有固化特性,不应该随外部状态改变而改变,这使得程序的逻辑复杂化。
2.将享元对象的状态外部化,而读取外部状态使得运行时间变长。使用场景:1.系统中有大量的相似对象,这些对象耗费大量的内存。
2.细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,即对象没有特定身份。
3.需要缓冲池的场景。
模板方法模式:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
优点:封装不变的部分,扩展可变的部分;提取公共部分代码,便于维护;行为由父类控制,子类实现。
应用场景:1.多个子类有公共方法,并且逻辑基本相同。
2.可以把重要的、复杂的、核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
3.重构时,模板方法模式是一个经常使用的模式,将相同的代码抽取到父类中。命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
优点:类间解耦;可扩展性;命令模式结合其他模式会更优秀。
缺点:可能会导致系统中出现过多的具体命令类,因此需要在项目中慎重考虑使用。应用场景:1.使用命令模式作为“回调”在面向对象系统中的替代;
2.需要在不同的时间指令请求、将请求排队; 3.系统需要支持的撤销(undo);
4.需要将系统中所有的数据更新操作保存到日志里,以便在系统崩溃时,可以根据日志读回所有的数据更新命令,重新调用execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新; 5.一个系统需要支持交易(transaction)。
责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,知道有对象处理它为止。
优点:责任链模式将请求和处理分开,请求者不知道是谁处理的,处理者可以不用知道请求的全貌;提高系统的灵活性。
缺点:1.降低程序的性能,每个请求都是从链头遍历到链尾,当链比较长时,性能大幅下降。2.不易于调试,由于采用了类似递归的方式,调试的时候逻辑比较复杂。
应用场景:一个请求需要一系列的处理工作;业务流的处理,例如,文件审批;对系统进行补充扩展。策略模式:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。
优点:提供了管理相关算法族的办法;提供了可以替换继承关系的方法;可以避免使用多重条件转换语句。
缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类;策略模式造成很多的策略类。
应用场景:多个类只是在算法或行为上稍有不同的场景;算法需要自由切换的场景;需要屏蔽算法规则的场景。
迭代器模式:提供一种方法访问一个容器对象中各个元素,又不需要暴露该对象的内部细节。优点:1.迭代器模式简化了访问容器元素的操作,具备一个统一的遍历接口。2.封装遍历算法,使算法独立于聚集角色。
缺点:1.迭代器模式给使用者一个序列化的错觉,从而产生错觉。2.迭代器的元素都是Object类型,没有类型特征。
中介者模式:用一个中介对象封装一系列对象(同事)的交互,中介者使对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变他们之间的交互。
优点:1.减少类间的依赖,将原有的一对多的依赖变成一对一的依赖,使得对象之间的关系更以维护和理解。
2.避免同事对象之间过度耦合,同事类只依赖于中介者,使同事类更易被复用,中介类和同事类可以相互独立地演化。
3.中介者模式将对象的行为和协作抽象化,将对象在小尺度的行为上与其他对象的相互作用分开处理。
缺点:1.中介者模式降低了同事对象的复杂性,但增加了中介者类的复杂性。
2.中介者类经常充满了各个具体同事类的关系协调代码,这种代码是不能复用的。注意事项:不应当在责任划分混乱时使用;不应当对数据类和方法类使用;正确理解封装。观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
优点:观察者和被观察者之间是抽象耦合;支持广播通信。缺点:1.如果一个主题有多个直接或者间接的观察者,则通知所有的观察者会花费很多时间,且开发和调试都比较复杂。
2.如果在主题之间有循环依赖,被观察者会触发他们间进行循环调用,导致系统崩溃。3.如果对观察者的通知是通过另外线程进行异步投递,系统必须保证投递的顺利执行。4.虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有提供相应的机制使观察者知道所观察的对象是如何发生变化。
应用场景:关联行为场景;事件多级触发场景;跨系统的消息交换场景。注意事项:广播链的问题;异步处理的问题。
备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样,以后就可以将该对象恢复到原先保存的状态。
应用场景:需要保存和恢复数据的相关场景状态;提供一个可回滚的操作;需要监控副本的场景;数据库连接的事务管理使用的就是备忘录模式。
注意事项:1.备忘录的生命周期。备忘录创建出来就要在最近的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。
2.备忘录的性能。不要频繁建立备份的场景中使用备忘录模式。
访问者模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
优点:1.使得增加新的操作变得很容易,增加新的操作只需要增加新的访问类。2.将有关的行为集中到一个访问者对象中,而不是分散到一个个元素类中。3.访问者模式可以跨过几个类的等级结构访问属于不同等级结构的成员类。4.积累状态。
缺点:增加新的元素类变得困难;破坏封装;违背依赖倒置原则。
应用场景:1.一个对象结构类有很多类对象,它们有不同的接口,当这些对象实施依赖于具体类的操作时,即使用迭代器模式不能胜任的场景下,可以采用访问者模式。
2.需要对一个对象结构中的对象进行很多不同且无关的操作,避免操作污染类。3.业务要求遍历多个不同的对象,这本身也是访问者模式的出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,执行不同的操作。
状态模式:当一个对象内在状态改变时允许改变行为,这个对象看起来像改变了其类型 优点:结构清晰;遵循设计原则;封装性非常好。缺点:子类太多,不易管理。
效果:1.状态模式需要对每一个系统可能取得的状态创建一个状态类的子类。2.由于每一个状态都被包装到了类里面,就可以不必采用过程性的处理方式,使长篇累牍的条件转移语句。
3.可以在系统的不同部分使用相同的一些状态类的对象。4.状态模式会造成大量的小状态类,但是可以使程序免于大量的条件转移语句,使程序实际上更易维护。
5.系统所选的状态子类均是从一个抽象状态类或借口继承而来,JAVA语言的特性使得在Java语言中使用状态模式较为完全,多态性原则是状态模式的核心。
使用场景:对象的行为依赖于它所处的状态,即行为随状态改变而改变的场景;对象在某个方法里依赖于一重或多重条件分支语句。
解释器模式:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
优点:简单的语法分析工具;扩展性,修改语法规则只要修改相应的非终结符表达式即可,若扩展语法,则只要增加非终结符类即可。
缺点:解释器模式会引起类膨胀;采用递归调用的方法。
使用场景:重复发生的问题可以使用解释器模式;一个简单语法需要解释的场景。
第二篇:Java设计模式学习心得
Java设计模式学习心得
阅读次数: 1584次 发布时间: 2010-04-10 14:52:13发布人: 网络转载
来源: 网络转载
整个设计模式贯穿一个原理:面对介面编程,而不是面对实现,(面向物件编程应该改爲面向介面编程)。目标原则是:降低耦合,增强灵活性。
一、创建模式
1.设计模式之Factory(工厂方法和抽象工厂)
使用工厂模式就象使用new一样频繁.2.设计模式之Prototype(原型)
用原型实例指定创建物件的种类,並且通过拷贝这些原型创建新的物件。
3.设计模式之Builder
汽车由车轮 方向盘 发动机很多部件组成,同时,将这些部件组装成汽车也是一件複杂的工作,Builder模式就是将这两种情况分开进行。
4.设计模式之Singleton(单态)
保證一个类只有一个实例,並提供一个访问它的全局访问点
二、结构模式
1.设计模式之Facade
可扩展的使用JDBC针对不同的资料库编程,Facade提供了一种灵活的实现。
2.设计模式之Proxy
以Jive爲例,剖析代理模式在用户级别授权机制上的应用
3.设计模式之Adapter
使用类再生的两个方式:组合(new)和继承(extends),这个已经在“thinking in java”中提到过。
4.设计模式之Composite
就是将类用树形结构组合成一个单位。你向别人介绍你是某单位,你是单位元元中的一个元素,别人和你做买卖,相当於和单位做买卖。文章中还对Jive再进行了剖析。
5.设计模式之Decorator
Decorator是个油漆工,给你的东东的外表刷上美丽的顔色。
6.设计模式之Bridge
将“牛郎织女”分开(本应在一起,分开他们,形成两个介面),在他们之间搭建一个桥(动态的结合)
7.设计模式之Flyweight
提供Java运行性能,降低小而大量重复的类的开销。
三、行爲模式
1.设计模式之Template
实际上向你介绍了爲什麽要使用Java 抽象类,该模式原理简单,使用很普遍。
2.设计模式之Memento
很简单一个模式,就是在记忆体中保留原来资料的拷贝。
3.设计模式之Observer
介绍如何使用Java API提供的现成Observer
4.设计模式之Chain of Responsibility
各司其职的类串成一串,好象击鼓传花,当然如果自己能完成,就不要推委给下一个。
5.设计模式之Command
什麽是将行爲封装,Command是最好的说明。
6.设计模式之State
状态是编程中经常碰到的实例,将状态物件化,设立状态变换器,便可在状态中轻鬆切换。
7.设计模式之Strategy
不同演算法各自封装,用户端可随意挑选需要的演算法。
8.设计模式之Mediator
Mediator很象十字路口的红绿灯,每个车辆只需和红绿灯交互就可以。
9.设计模式之Interpreter
主要用来对语言的分析,应用机会不多。
10.设计模式之Visitor
访问者在进行访问时,完成一系列实质性操作,而且还可以扩展。
11.设计模式之Iterator
这个模式已经被用来遍曆Collection中物件。使用频率很高。在Java中无需专门阐述,在大多数场合也无需自己制造一个Iterator,只要将物件装入Collection中,我们就直接可以使用Iterator模式。
第三篇:java设计模式考试题
一选择题:共10分
1.Open-Close原则的含义是一个软件实体(a)
A.应当对扩展开放,对修改关闭
2.是10道选择题第一题考完了
二 简答题 共10分
1.什么是设计模式?
设计模式是从许多优秀软件系统中总结出的成功的可复用的设计方案。
2.设计模式的四个基本要素是什么,以及他们是什么含义?
名称:
问题:
方案:
效果:含义在书上p1
三 画类图
14画出命令模式的类图,并写出关键的代码?
见书上17页
15.画出迭代器模式的类图,并写出关键的代码?、见书上105页
四写代码学一个关于求职中心,通知求职者的问题
这是一个观察者模式,具体看看书
我的书是第五章的书上例题
2.是一个交通工具营销问题;
应该是外观模式;见书上99页,应该就是个
这是我们学校的考试题,2012年
第四篇:JAVA设计模式之创建模式
设计模式之Builder
Builder模式定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到.为何使用?
是为了将构建复杂对象的过程和它的部件解耦.注意: 是解耦过程和部件.因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开.如何使用?
首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示.首先,需要一个接口,它定义如何创建复杂对象的各个部件: public interface Builder {
//创建部件A 比如创建汽车车轮
void buildPartA();
//创建部件B 比如创建汽车方向盘
void buildPartB();
//创建部件C 比如创建汽车发动机
void buildPartC();
//返回最后组装成品结果(返回最后装配好的汽车)
//成品的组装过程不在这里进行,而是转移到下面的Director类中进行.//从而实现了解耦过程和部件
Product getResult();} 用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品: public class Director {
private Builder builder;
public Director(Builder builder){
this.builder = builder;} // 将部件partA partB partC最后组成复杂对象 //这里是将车轮 方向盘和发动机组装成汽车的过程 public void construct(){
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
} } Builder的具体实现ConcreteBuilder: 通过具体完成接口Builder来构建或装配产品的部件;定义并明确它所要创建的是什么具体东西;提供一个可以重新获取产品的接口: public class ConcreteBuilder implements Builder {
Part partA, partB, partC;public void buildPartA(){
//这里是具体如何构建partA的代码
};public void buildPartB(){
//这里是具体如何构建partB的代码 };public void buildPartC(){
//这里是具体如何构建partB的代码 };public Product getResult(){
//返回最后组装成品结果 };} 复杂对象:产品Product: public interface Product { } 复杂对象的部件: public interface Part { }
我们看看如何调用Builder模式: ConcreteBuilder builder = new ConcreteBuilder();Director director = new Director(builder);
director.construct();Product product = builder.getResult();Builder模式的应用
在Java实际使用中,我们经常用到“池”(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池.“池”实际是一段内存,当池中有一些复杂的资源的“断肢”(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些“断肢”,将提高内存使用效率,提高池的性能.修改Builder模式中Director类使之能诊断“断肢”断在哪个部件上,再修复这个部件.设计模式之Factory
定义:提供创建对象的接口.为何使用?
工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。
为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A()工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。我们以类Sample为例,如果我们要创建Sample的实例对象: Sample sample=new Sample();可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。
首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成: Sample sample=new Sample(参数);但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。
在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。
这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample.我们要实例化他们时,如下: Sample mysample=new MySample();Sample hissample=new HisSample();随着项目的深入,Sample可能还会“生出很多儿子出来”, 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了.工厂方法
你会建立一个专门生产Sample实例的工厂: public class Factory{
public static Sample creator(int which){
//getClass 产生Sample 一般可使用动态类装载装入类。if(which==1)
return new SampleA();else if(which==2)
return new SampleB();
} } 那么在你的程序中,如果要实例化Sample时.就使用 Sample sampleA=Factory.creator(1);这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample:
进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory了。抽象工厂
工厂模式中有: 工厂方法(Factory Method)抽象工厂(Abstract Factory).这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.这里假设:Sample有两个concrete类SampleA和SamleB,而Sample2也有两个concrete类Sample2A和SampleB2 那么,我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的Factory拓展成抽象工厂: public abstract class Factory{
public abstract Sample creator();
public abstract Sample2 creator(String name);} public class SimpleFactory extends Factory{
public Sample creator(){
.........return new SampleA } public Sample2 creator(String name){
.........return new Sample2A } } public class BombFactory extends Factory{
public Sample creator(){
......return new SampleB } public Sample2 creator(String name){
......return new Sample2B } }
从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2? 抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,举例
我们以Jive的ForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式: public abstract class ForumFactory {
private static Object initLock = new Object();
private static String className = “com.jivesoftware.forum.database.DbForumFactory”;
private static ForumFactory factory = null;
public static ForumFactory getInstance(Authorization authorization){
//If no valid authorization passed in, return null.if(authorization == null){
return null;
}
//以下使用了Singleton 单态模式
if(factory == null){
synchronized(initLock){
if(factory == null){
......}
}
} try {
//动态转载类
Class c = Class.forName(className);
factory =(ForumFactory)c.newInstance();} catch(Exception e){
return null;}
//Now, 返回 proxy.用来限制授权对forum的访问
return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));
}
//真正创建forum的方法由继承forumfactory的子类去完成.public abstract Forum createForum(String name, String description)
throws UnauthorizedException, ForumAlreadyExistsException;
....}
因为现在的Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口: private static String className = “com.jivesoftware.forum.database.DbForumFactory”;你可以使用自己开发的创建forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果.看看Java宠物店中的CatalogDAOFactory: public class CatalogDAOFactory {
/**
* 本方法制定一个特别的子类来实现DAO模式。
* 具体子类定义是在J2EE的部署描述器中。
*/
public static CatalogDAO getDAO()throws CatalogDAOSysException {
CatalogDAO catDao = null;
try {
InitialContext ic = new InitialContext();//动态装入CATALOG_DAO_CLASS //可以定义自己的CATALOG_DAO_CLASS,从而在无需变更太多代码 //的前提下,完成系统的巨大变更。
String className =(String)ic.lookup(JNDINames.CATALOG_DAO_CLASS);
catDao =(CatalogDAO)Class.forName(className).newInstance();
} catch(NamingException ne){
throw new CatalogDAOSysException(“
CatalogDAOFactory.getDAO: NamingException while
getting DAO type : n” + ne.getMessage());
} catch(Exception se){
throw new CatalogDAOSysException(“
CatalogDAOFactory.getDAO: Exception while getting
DAO type : n” + se.getMessage());
}
return catDao;
} } CatalogDAOFactory是典型的工厂方法,catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类,这个实现子类在Java宠物店是用来操作catalog数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与CATALOG_DAO_CLASS变量就可以。
由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。
设计模式之Prototype(原型)
定义: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。如何使用? 因为Java中的提供clone()方法来实现对象的克隆(具体了解clone()按这里),所以Prototype模式实现一下子变得很简单.以勺子为例:
public abstract class AbstractSpoon implements Cloneable {
String spoonName;
public void setSpoonName(String spoonName){this.spoonName = spoonName;}
public String getSpoonName(){return this.spoonName;}
public Object clone()
{
Object object = null;
try {
object = super.clone();
} catch(CloneNotSupportedException exception){
System.err.println(“AbstractSpoon is not Cloneable”);
}
return object;
} } 有两个具体实现(ConcretePrototype): public class SoupSpoon extends AbstractSpoon {
public SoupSpoon()
{
setSpoonName(“Soup Spoon”);
} } public class SaladSpoon extends AbstractSpoon {
public SaladSpoon()
{
setSpoonName(“Salad Spoon”);
} } 调用Prototype模式很简单: AbstractSpoon spoon = new SoupSpoon();AbstractSpoon spoon = new SaladSpoon();当然也可以结合工厂模式来创建AbstractSpoon实例。
在Java中Prototype模式变成clone()方法的使用,由于Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很自然,两者已经几乎是浑然一体了。这反映在很多模式上,如Interator遍历模式。
设计模式之Singleton(单态)
定义: Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。
还有, singleton能够被状态化;这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。
另外方面,Singleton也能够被无状态化。提供工具性质的功能,Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。
我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。如何使用?
一般Singleton模式通常有几种形式: public class Singleton {
private Singleton(){}
//在自己内部定义自己一个实例,是不是很奇怪?
//注意这是private 只供内部调用
private static Singleton instance = new Singleton();
}
第二种形式: public class Singleton {
private static Singleton instance = null;public static synchronized Singleton getInstance(){
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次
//使用时生成实例,提高了效率!if(instance==null)
instance=new Singleton();return instance;} //这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInstance(){
return instance;
} }
使用Singleton.getInstance()可以访问单态类。
上面第二中形式是lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。
注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking(DCL)的讨论,有兴趣者进一步研究。
一般认为第一种形式要更加安全些。使用Singleton注意事项:
有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。
我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocator为例稍微分析一下:
在Pet Store中ServiceLocator有两种,一个是EJB目录下;一个是WEB目录下,我们检查这两个ServiceLocator会发现内容差不多,都是提供EJB的查询定位服务,可是为什么要分开呢?仔细研究对这两种ServiceLocator才发现区别:在WEB中的ServiceLocator的采取Singleton模式,ServiceLocator属于资源定位,理所当然应该使用Singleton模式。但是在EJB中,Singleton模式已经失去作用,所以ServiceLocator才分成两种,一种面向WEB服务的,一种是面向EJB服务的。
Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类 线程 内存等概念有相当的了解。进一步深入可参考:
Double-checked locking and the Singleton pattern When is a singleton not a singleton?
第五篇:JAVA学习书籍- 设计模式
谈到设计模式很多人多会推荐GOF 的那本,该书在Amzon上是五星级的推荐书籍。不过对于学习java 没多久的、特别是java 初学者,我很不推荐这本书。主要是该书的例子基本都是C++的,很多细节没有讲述得足够清楚。
我给大家推荐的第一本是阎宏博士的《Java 与模式》,它是第一本中国人自己写的关于设计模式的书籍,写的比较有趣,融合了很多中
华民族的文化和观念,例子、类图都比较多,且相对简单!非常不错的入门书籍――又是大块头哦!
其次我推荐Wiley 出版社出版的《Pattern In Java》一套三本,我才看了第一本,好像第二本不怎么样,第三本还不错!
第三本是中文翻译版的关于多线程模式的(很难得的中文翻译版)中国铁道出版社2003 年出版的《Java 多线程设计模式》,将多线程模
式讲得非常浅显,配有大量的图例,每章都有习题,最后有答案!我研究多线程模式就是由它开始的!
第四本,今年出版的Head First 系列的《Head First Design Pattern》,秉承Head First 系列图书的优点,大量的类图、丰富的实例、有趣的注解,值得购买!
其次在J2EE 方向你可以研究阅读Addison Wesley 2002 年出版的《Patterns of Enterprise Application Architecture》,众多大腕的作品,讲企业消息集成的!Sun 提供的《J2EE PATTERNS SL500》也很好!晚了推荐那一本Amzon 4 星半的《Holub on patterns》,大师的作品,提供了,很值得研究的例子,不过对上面四本不是很熟悉的读者,最好不要读它!可能会让你比较累!
我学习设计模式经过一段很曲折的路线,前前后后大约看了20 本,阎宏博士的《Java 与模式》我看了4 遍,还排除我第一次基本没看
懂的看!记得研一时老师给我们讲了GOF 的那本,作为选修课,我和它们计算机系的硕士、博士们一起,到最后一个班40-50 个人,不
超过3 个人明白,我也没有明白任何一点(基础差吧――主要我对C++语言一点都不了解),凭我不伏输的性格,我认为我对java 语言理
解还可以,我就借了《Java 与模式》,结果还是基本没看懂。很有幸的是读研三时,听过了上交大饶若楠老师关于Java OOP 语言的讲座,我懂了组合书籍模式等三种设计模式后,对其它模式有了强烈的兴趣和要征服它的愿望!工作后我买的第一本就是《Java 与模式》,第一遍花了2 个月研究了这个1000 多页的大块头,后来第三遍15 天左右就可以搞定,笔记记了一大本!从此一发不可收拾。
选对书、埋头研究。相信很快就会入门的!
学习Java 语言8 个简单的部分,这只是我们研究Java 语言的开始!这些都懂了充其量一个java 程序员而已,后面的路很长很长!我们
可以继续研究数据库实现的源代码、Servlet 服务器的源代码、RMI、EJB、JNDI、面向方面编程、重构、ANT 工具、Eclipse 工具、Spring
工具、JBoss、JOnAS、Apache Geronimo 等J2EE 服务器!研究了这些你可能会成为一个出色的J2EE Architecture!你可以继续研究剖
析器、编译器、JNODE(java 写的操作系统)