Java设计模式及应用案例(第2版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-07 17:11:44

点击下载

作者:金百东 刘德山

出版社:人民邮电出版社有限公司

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Java设计模式及应用案例(第2版)

Java设计模式及应用案例(第2版)试读:

软件工程Java设计模式及应用案例(第2版)金百东 刘德山 编著人民邮电出版社北京

图书在版编目(CIP)数据

Java设计模式及应用案例 / 金百东,刘德山编著.--2版.北京:人民邮电出版社,2017.11

ISBN 978-7-115-46258-9

Ⅰ.①J… Ⅱ.①金… ②刘… Ⅲ.①JAVA语言—程序设计 Ⅳ.①TP312.8

中国版本图书馆CIP数据核字(2017)第155552号◆ 编著 金百东 刘德山责任编辑 邹文波责任印制 陈犇◆ 人民邮电出版社出版发行 北京市丰台区成寿寺路11号邮编 100164  电子邮件 315@ptpress.com.cn网址 http://www.ptpress.com.cn三河市中晟雅豪印务有限公司印刷◆ 开本:787×1092 1/16印张:21.25  2017年11月第2版字数:544千字  2017年11月河北第1次印刷定价:59.00元读者服务热线:(010)81055256 印装质量热线:(010)81055316反盗版热线:(010)81055315广告经营许可证:京东工商广登字20170147号内容提要

设计模式是一套被重复使用的代码设计经验的总结。本书面向有一定Java语言基础和一定编程经验的读者,旨在培养读者良好的设计模式和思维方式,加强对面向对象思想的理解。

全书共23章,首先强调了接口和抽象类在设计模式中的重要性,介绍了反射技术在设计模式中的应用,然后对常用的24个设计模式逐一进行了详细的讲解,包括:6个创建型模式、11个行为型模式、7个结构型模式。

本书理论讲解透彻,应用示例深入浅出。设计模式的讲解均从生活中的一类常见事物的分析引出待讨论的主题,然后深入分析设计模式,最后进行综合应用示例分析。应用示例部分所有案例都源自应用项目,内容涉及Java、JSP、JavaScript、Ajax等实用技术,知识覆盖面广。

本书可供高等院校计算机相关专业本科生和研究生设计模式、软件体系结构等课程使用,对高级程序员、软件工程师、系统架构师等专业人员也具有一定的参考价值。第2版前言

创作背景

设计模式是从许多优秀的软件系统开发过程中总结出的成功的、可复用的设计方案。应用设计模式构建有弹性、可扩展的应用系统已经成为软件开发人员的共识,越来越多的系统架构师有掌握设计模式内容的迫切需求。近年来,市场上涌现了一些有关设计模式的图书。这些图书多以诙谐幽默的生活实例入手,让读者对所述设计模式有一定的感性认识,然后引入设计模式的特点,最后通过计算机程序进行理性说明。这些示例,内容讲解充分,但专业应用部分稍显单薄,编者认为主要有以下几点不足。第一,示例偏简单,读者易理解其含义,但较难达到应用层次;第二,示例趣味性不足,很多讲解就是命令行界面,而实际应用的图形界面很少涉及;第三,有些书以ERP各个具体模块讲解设计模式,显得太单一。上述原因是促使我们写作本书的动力。

再版缘由

本书第1版《Java设计模式深入研究》,由人民邮电出版社于2014年出版,从学生学习效果和网上评价来看,得到了很多读者的认可。但该书仅介绍了12种设计模式,还有12个常用的设计模式未涉及,这是本次再版最主要的缘由。而且,第一版的写作风格和示例也得到了读者的肯定,很多读者希望看到完整的设计模式图书,于是,我们进行了本书的再版编写工作,写作内容更倾向于介绍应用案例。

本书内容

本书首先讲解了接口和抽象类在设计模式中的重要性,以及必备的反射技术。然后精讲了常用的24个经典设计模式,包括:6个创建型模式、11个行为型模式、7个结构型模式。每个模式一般都包含以下四部分。(1)问题的提出:一般从生活中的一类常见事物引出待讨论的主题。(2)模式讲解:用模式方法解决与之对应的最基本问题,归纳出角色及UML类图。(3)深入理解模式:讲解笔者对模式的一些体会。(4)应用示例:均是实际应用中较难的程序,进行了详细的问题分解、分析与说明。

与第1版比较,第2版增加了如下内容。(1)增加讲解了12个设计模式。包括:单例模式、原型模式、责任链模式、迭代器模式、中介者模式、备忘录模式、策略模式、模版方法模式、解释器模式、享元模式、适配器模式、外观模式。(2)在每章的章前语中,给出了模式的定义及适应的场景,使读者对每个模式功能有一个初步的了解,增加学习兴趣。(3)每章都增加了模式实践练习环节,使读者在实践中增加对设计模式的理解。(4)将第1版的第1、2章内容合并为第2版的第1章设计模式概述内容,这样本书的体系更为合理。

本书特色(1)示例丰富,讲解细致,不同的设计模式融于各个案例之中,并结合案例进行讲解和拓展。示例既涉及命令行程序,也有图形界面、Web程序等;技术涉及Java、JSP、JavaScript、Ajax等。(2)强调了语义的作用。一方面把设计模式抽象思想转化成日常生活中最朴实的语言;另一方面把生活中对某事物“管理”的语言转译成某设计模式。相比而言,后者更为重要。(3)强调了反射技术的作用。对与反射技术相关的设计模式均做了详细的论述。(4)编者在文中多处谈了学习设计模式的心得,会对读者有较大的启发。

学习设计模式的方法(1)在清晰理解设计模式基础知识的基础上,认真实践每一个应用示例,并加强分析与思考。(2)学习设计模式不是一朝一夕的事,不能好高骛远。它是随着程序设计人员思维的发展而发展的,一定要在项目中亲身实践,从量变到质变,谨记“纸上得来终觉浅,绝知此事要躬行”。(3)加强基础知识训练,如数据结构、常用算法等的训练。基础知识牢固后,学习任何新事物,内心都不会发慌,都有信心战胜它。否则知识学得再多,也只是空中楼阁。(4)不要为了模式而模式,要在项目中综合考虑,统筹安排。

关于本书的使用(1)由于篇幅关系,有些程序的导入(import)命令在本书的示例中没有给出,这些语句需要读者自行加入,但给出的程序源码是完整的。(2)在使用反射时,例如,使用XML文件或properties文件封装类的配置信息时,如果被封装的类在一个包中,应在配置文件中类的名字前指明该类所属的包,这样程序才能顺利运行。(3)连接数据库的工具类DbProc位于生成器模式一章(第4章)中,全书通用。如果需要,复制到相应的项目中即可。(4)每个模式中有很多示例,每个示例中所有文件(包括可能需要的配置文件)都在同一目录下。读者在测试时,可自己在工程目录下建相应的包,将每个示例中的文件复制到相应包下即可。

总之,设计模式是一门重要的计算机软件开发技术,编者希望尽微薄之力,为设计模式研究添砖加瓦。但由于水平有限,时间紧迫,书中难免有不妥或疏漏之处,恳请广大读者批评指正。编者2017年9月第1章 设计模式概述

本章简要介绍了设计模式的历史及分类,强调了接口和抽象类在设计模式中的作用,介绍了反射及配置文件解析技术,为后续讲述具体的设计模式做铺垫。1.1 设计模式简介

1994年,由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四人合著出版了一本名为Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:《设计模式——可复用的面向对象软件元素》)的书,该书首次提到了软件开发中设计模式的概念。他们所提出的设计模式主要是基于以下的面向对象设计原则。(1)对接口编程而不是对实现编程;(2)优先使用对象组合而不是继承。

常用设计模式共有24种,这些模式可以分为3大类:创建型模式、行为型模式、结构型模式。

创建型模式提供了一种在创建对象的同时,隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时,更加灵活。常用的创建型模式包括:单例模式、简单工厂模式、工厂模式、抽象工厂模式、生成器模式,以及原型模式。

行为型模式特别关注对象之间的通信。常用行为型模式包括:责任链模式、命令模式、迭代器模式、访问者模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模版方法模式,以及解释器模式。

结构型模式关注类和对象的组合,继承的概念被用来组合接口和定义组合对象获得新功能的方式。常用结构型模式包括:享元模式、适配器模式、组合模式、代理模式、桥接模式、装饰器模式,以及外观模式。

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的,以及具有代码设计经验的总结。使用设计模式是为了重用代码,让代码更容易被他人理解,保证代码的可靠性。设计模式使代码编制真正工程化,是软件工程的基石。每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。1.2 预备知识

设计模式需要许多基础知识,如算法和数据结构等,但这里着重强调接口、抽象类以及反射的作用。1.2.1 接口和抽象类

接口与抽象类是面向对象思想的两个重要概念。接口仅是方法定义和常量值定义的集合,方法没有函数体;抽象类定义的内容理论上比接口中的内容要多得多,可定义普通类包含的所有内容,还可定义抽象方法,这也正是叫作抽象类的原因。接口、抽象类本身不能实例化,必须在相应子类中实现抽象方法,才能获得应用。

那么,如何更好地理解接口与抽象类呢?如:接口中能定义抽象方法,为什么还要抽象类?抽象方法无函数体,不能实例化,说明接口、抽象类本身没有用途,这样不是显得多余吗?接口与抽象类的关系到底如何?获得这些问题答案最好的办法就是生活实践。例如:写作文的时候,一定要思考好先写什么,后写什么;做几何题的时候,要想清楚如何引辅助线,用到哪些公理、定理等;做科研工作的时候,一定要思索哪些关键问题必须解决;工厂生产产品前,必须制定完善的生产计划等。也就是说,人们在做任何事情前,一般来说是先想好,然后再去实现。这种模式在生活中是司空见惯的,因此Java语言一定要反映“思考—实现”这一过程是通过不同关键字来实现的,即用接口(Interface)、抽象类(Abstract)来反映思考阶段;用子类(Class)来反映实现阶段。

从上文易得出:“思考—实现”是接口、抽象类的简单语义,从此观点出发,结合生活实际,可以方便回答如下许多待解答的问题。

为什么接口、抽象类不能实例化?由于接口、抽象类是思考的结果,只是提出了哪些问题需要解决,勿需函数体具体内容,当然不能实例化了。

为什么接口、抽象类必须由子类实现?提出问题之后,总得有解决的方法吧。Java语言是通过子类来实现的,当然一定要解决“思考”过程中提出的所有问题。

接口、抽象类有什么区别?可以这样考虑,人类经思考后提出的问题一般有两类:一类是“顺序”问题;另一类是“顺序+共享”问题;前者是用接口描述的;后者是用抽象类来描述的。

图1-1描述了一个生产小汽车具体的接口示例,如下所示。图1-1 生产小汽车接口示例

图1-1(a)表明生产小汽车可由钢板切割、压模、组装和喷漆四个工序组成。这些工序是顺序关系,因此转化成接口是最恰当的,如图1-1(b)所示。

抽象类与接口不同,假设我们要组装多种价位的计算机,其配置参数如图1-2所示。图1-2 计算机抽象类示例

可以看出:要配置n类计算机。其中,每类计算机的硬盘、光驱、显示器都是不同类型的,是并列结构(也可以看作是顺序结构),但由于主板是相同类型的,属于共享结构,因此,转化成的抽象类如下所示。1.2.2 反射

1.反射的概念

Java反射(Java Reflection)是指在程序运行时获取已知名称的类或已有对象相关信息的一种机制,包括类的方法、属性、父类等信息,还包括实例的创建和实例类型的判断等。在常规的程序设计中,我们调用类对象及相关方法都是显式调用的。例如:

那么能否根据类名A,①列出这个类有哪些属性和方法;②对于任意一个对象,调用它的任意一个方法。这也即是“反过来映射——反射”的含义。

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中。

●Class类:代表一个类。

●Constructor类:代表类的构造方法。

●Field类:代表类的成员变量(成员变量也称为类的属性)。

●Method类:代表类的方法。

2.统一形式调用

运用上述Class、Constructor、Field、Method四个类,能解析无穷多的系统类和自定义类结构,创建对象及方法执行等功能,而且形式是一致的。【例1-1】统一形式解析类的构造方法、成员变量、成员方法。

●A是自定义类。首先通过静态方法Class.forName("A")返回包含A类结构信息的Class对象classInfo,然后通过Class类中的getConstructors()方法获得A类的构造方法信息,通过getDeclaredFields()方法获得类A的成员变量信息,通过getDeclaredMethods()方法获得类A的成员方法信息。获得其他类的结构信息步骤与上述是相似的,因此形式是统一的。

●上述程序仅是解析了A类的结构,并没有产生类A的实例。怎样用反射机制产生类A的实例呢?请参考如下示例。【例1-2】统一形式调用构造方法示例。

●可以看出:反射机制有两种生成对象实例的方法。一种是通过Class类的无参getConstructors()方法,获得Constructor对象数组,其长度等于反射类中实际构造方法的个数。示例中,cons[0]~cons[2]分别对应无参、单参数、双参数3个构造方法,分别调用newInstance()方法,才真正完成3个实例的创建过程。另一种是通过Class类的有参getConstructor()方法,来获得对应的一个构造方法信息,然后调用newInstance()方法,完成该实例的创建过程。

●加深对Class类中getConstructor()方法参数的理解,其原型定义如下所示。

其中,parameterTypes表示必须指明构造方法的参数类型,可用Class数组或依次输入形式来表示传入参数类型。

若示例中要产生A(String s, Integer m)构造方法的实例,由于第1个参数是字符串,第2个参数类型是整形数的包装类,则依次传入参数类型的调用方法如下所示。

若传入的是数组类型,则调用方法如下所示。

●加深对Constructor类中newInstance()方法参数的理解,其原型定义如下所示。

其中,initargs表示必须指明构造方法的参数值(非参数类型),可用Object数组或依次输入形式来表示传入参数值。若示例中要产生A(String s, Integer m)构造方法的实例,由于第1个参数是字符串数值,第2个参数是整形数值,则依次传入参数类型的调用方法如下所示。

若传入的是数组值,则调用方法如下所示。

●通过文中两种创建实例的方法对比,第2种方法更好,即先用有参getConstructor()方法获得构造方法的信息,再用有参newInstance()方法产生类的实例。【例1-3】统一形式调用成员方法示例。

方法反射主要是利用Class类的getMethod()方法,得到Method对象,然后利用Method类中的invoke()方法完成反射方法的执行。getMethod()、invoke()方法原型及使用方法与getConstructor()是类似的,参见上文。【例1-4】一个通用方法。

分析:只要知道类名字符串、方法名字符串、方法参数值,运用反射机制就能执行该方法,程序如下所示。

通过该段程序,可以看出反射机制的突出特点是:可以把类名、方法名作为字符串变量,直接对这两个字符串变量进行规范操作,就能产生类的实例及相应的运行方法,这与普通的先new实例,再直接调用所需方法有本质的不同。

运用反射技术编程,可以大大提高应用框架的稳定程度。具体表现在:当新增加功能的时候,仅需增加相应的功能类,而框架调用可能不需要发生变化。请看下面示例【例1-5】。【例1-5】已知接收字符串可有两种走向,既可以在屏幕输出,又可以将字符串保存至文件。要求:从命令行输入字符串处理类的类名字符串。若输入“ConsoleMsg”,则将字符串输出到控制台;若输入“FileMsg”,则将字符串输出到文件。

为了实现上述功能,可定义共同的字符串处理接口IMsg,两个子类ConsoleMsg、FileMsg均实现IMsg接口。具体实现代码如下所示。

着重加深对测试类代码的理解。其中,最重要的一行代码如下所示。

它表明产生了类名是args[0]的一个实例:可能是ConsoleMsg,可能是FileMsg,也可能是其他字符串处理类的一个实例。也就是说,该行表明的含义是动态的,不随哪个具体的IMsg接口的子类改变而改变。从框架角度来说,它是稳定的。例如:现在要增加一个将字符串保存至数据库的类,只需增加功能子类DbMsg,而测试类代码勿需改变。

本例中,产生字符串处理类对象的实例用了反射技术,不过调用方法时,并没有用到反射技术,而是用到了接口技术。反射技术固然强大,但它是以牺牲时间为代价的,代码也不易理解。一般来说,运用“接口+构造方法反射”就足以编制功能强大的框架代码了。

因此,如果把某些动态参数封装在配置文件中,通过读取配置文件获得所需参数,再运用反射技术,就可以编制更加灵活的代码,这是下文即将论述的内容。

3.反射与Properties配置文件

Properties格式文件是Java常用的配置文件,是简单的文本格式。它是用来在一个文件中存储键-值对的。其中,键和值是用等号分隔的。JDK中利用系统类Properties来解析Properties文件。Properties类是Hashtable的一个子类,用于键keys和值values之间的映射。Properties类表示了一个持久的属性集,属性列表中每个键及其键值都是一个字符串。其常用函数如下所示。

●Properties():创建一个无默认值的空属性列表。

●void load(InputString inStream):从输入流中读取属性列表。

●String getProperty(String key):获取指定键key的键值,以字符串的形式返回。

●void setProperty(String key, String value):设置对象中key的键值。【例1-6】重新实现【例1-5】的功能。要求定义Properties文本文件msg.properties,其中添加一个键值对:func=ConsoleMsg。也就是说,字符串处理类的类名保存在配置文件中,而不是由控制台输入。

为了实现上述功能,编制的接口和类代码如下所示。

Properties还支持如表1-1所示的简单XML格式文件(重新定义msg.properties文件为msg.xml)。表1-1 msg.xml定义

其XML文件要求如下:一个properties标签,另一个comment注释子标签,然后是任意数量的标签。每一个标签,都有一个键属性,输入的内容就是它的值。利用Properties类解析该文件与解析老式文本文件的方法几乎是一致的,只不过用loadFromXML()方法代替了load()方法,具体代码如下所示。模式实践练习

1.如何更好地理解接口和抽象类的作用,你还能说出哪些与1.2.1节不同的内容?

2.利用反射技术解析向量类java.util.Vecor有哪些属性和方法。第2章 单例模式

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。当系统需要某个类只能有一个实例时,就可以采用单例模式。2.1 问题的提出

生活中经常遇到这样的现象:一个国家只能有一个主席职务,一个大学只能有一个校长,一个单位只能有一个公章等。也就是说,在我们的生活中,某些事物具有唯一性。如果多于一个的话,就会引起许多意想不到的结果。这种现象在生活中是普遍存在的,而在计算机程序设计中,就是我们即将讲到的单例模式。2.2 单例模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类,保证系统中该类只有一个实例。这一点有时是非常重要的。例如学号生成器:学号对每个学生而言是非常重要的,不允许有重复,因此学号生成器对象最好是唯一的。若不唯一,那么一方面浪费了内存,另一方面又增加了学号查重的额外编码维护。再如一个系统中可以存在多个打印任务,但是只能有一个正在打印的打印任务;C++中的标准键盘输入对象是唯一的cin,标准屏幕输出对象是唯一的cout。

单例模式的UML类图,如图2-1所示。

总之,单例模式具有如下特点:①单例类只能有一个实例;②单例类必须自己创建自己的唯一实例;③单例类必须给所有其他对象都能提供这一实例。图2-1 单例设计模式类图2.3 单例模式的实现方式

保证单例模式仅有一个实例的核心思想是构造方法私有化,即不允许外部调用该类的构造方法。基于此思想,实现单例模式的方法主要有以下两种。

1.直接实例化

直接实例化实现单例模式的代码如下所示。

代码关键点描述如下所示。

●构造方法Singleton()被定义成private,避免了外部调用,这是实现单例对象的关键。

●直接定义了静态成员变量single,并通过new Singleton()完成了初始化,之后不再变化,因此对象single是线程安全的。

●外部类可通过静态getInstance()方法返回单例对象的实例。

2.延迟实例化

延迟实例化实现单例模式的代码如下所示。

与直接实例化稍有不同,单例成员变量single首先初始化为null,它是在方法getInstance()内部完成延迟实例化的,并返回单例对象,但是该方法存在线程安全问题。例如,假设两个线程调用getInstance()方法,第一个线程执行完语句if(single==null),条件成立,在执行实例化语句single=new Singleton2()前;第二个线程执行语句if(single==null),条件也成立,因此两个线程都要执行单例对象实例化语句single=new Singleton2(),这样就产生了两次单例对象,而我们要求需要在应用程序执行过程中仅产生一次单例对象,如何解决呢?常用方法有三种,如下所示。

方法1

完全同步方法,代码如下所示。

很明显,getInstance()方法是完全同步方法。当多线程同时访问synchronized方法getInstance()方法的时候,多线程是“串行”运行的,一个线程必须完全执行完getInstance()方法,下一个线程才能调用getInstance()方法。当第一个线程调用getInstance()方法时,由于single是null,因此实例化单例对象single=new Singleton2(),而其他线程“串行”运行该方法时,由于single不为null(已经完成了实例化),因此直接返回单例对象single给调用者即可。

方法2

部分同步方法,代码如下所示。

部分同步方法的核心思想是在方法getInstance()内有些代码可并行运行,有些代码可同步(串行)运行。方法2部分同步方法与方法1完全同步方法相比,提高了运行效率。本方法是通过双重锁部分同步机制获得单例对象的。因为代码中有两行相同的语句if(single==null),故而叫作双重锁。第一个if语句可并行运行,当多线程均满足该条件时,synchronized(Singleton.class)修饰的代码块必须串行运行;第二个if语句要再次确认单例对象是否为null,若为null则创建并返回单例对象,否则直接返回单例对象。

方法3:静态内部类,代码如下所示。

与方法1、方法2相比,静态内部类最大的不同在于Singleton3类中无synchronized关键字,因而提高了Java虚拟机的维护效率。它是通过静态内部类My来实现单例对象的,而且对象My.single是线程安全的。为了更好地理解静态内部类实现单例对象的特点,就需要编制如下所示的一个简单测试类。

该测试类的功能是:首先初始化标准键盘输入。当运行到s.nextLine()时,标准输出无任何显示;当随意输入一些字符,按回车后,程序继续运行;当运行到obj=Singleton3.getInstance()时,程序调用My.single,这时才运行静态内部类My中的代码Singleton3 single=new Singleton3(),从而产生单例对象,并返回;当运行到obj2=Singleton3.getInstance()时,程序再次调用My.single,这时不再调用静态内部类My中的代码,直接返回已产生的单例对象。

通过该测试类可反映出:当Java虚拟机加载应用程序字节码时,单例对象并不是立即加载的,当第一次运行My.single时,单例对象才动态生成。在上文直接实例化方法中,Singleton类中直接定义了成员变量private static final Singleton single=new Singleton(),导致Java虚拟机加载应用程序字节码时,单例对象直接生成。因此从虚拟机效率来说,利用静态内部类生成单例对象是更优的。2.4 应用示例【例2-1】编制日志类。一般来说,应用程序都有日志文件,记录一些执行信息,该功能利用单例对象来实现是比较恰当的。本例实现最基本的功能,包括:记录时间及相关内容字符串,其代码如下所示。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载