工厂模式Factory定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,FactoryMethod使一个类的实例化延迟到其子类。作用:扩展性和维持性应用场景考虑这样一个实际应用:实现一个导出数据的应用框架,来让客户选择数据的导出方式,并真正执行数据导出。在一些实际的企业应用中,一个公司的系统往往分散在很多个不同的地方运行,比如各个分公司或者是门市点,公司没有建立全公司专网的实力,但是又不愿意让业务数据实时的在广域网上传递,一个是考虑数据安全的问题,一个是运行速度的问题。这种系统通常会有一个折中的方案,那就是各个分公司内运行系统的时候是独立的,是在自己分公司的局域网内运行。然后在每天业务结束的时候,各个分公司会导出自己的业务数据,然后把业务数据打包通过网络传送给总公司,或是专人把数据送到总公司,然后由总公司进行数据导入和核算。通常这种系统,在导出数据上,会有一些约定的方式,比如导出成:文本格式、数据库备份形式、Excel格式、Xml格式等等。现在就来考虑实现这样一个应用框架。在继续之前,先来了解一些关于框架的知识。模式结构和说明工厂方法模式的结构如图3所示:图3工厂方法模式结构示意图Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。ConcreteProduct:具体的Product接口的实现对象。Creator:创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象。ConcreteCreator:具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例。工厂方法模式示例代码(1)先看看Product的定义,示例代码如下:/***工厂方法所创建的对象的接口*/publicinterfaceProduct{//可以定义Product的属性和方法}(2)再看看具体的Product的实现对象,示例代码如下:/***具体的Product对象*/publicclassConcreteProductimplementsProduct{//实现Product要求的方法}(3)接下来看看创建器的定义,示例代码如下:/***创建器,声明工厂方法*/publicabstractclassCreator{/***创建Product的工厂方法*@returnProduct对象*/protectedabstractProductfactoryMethod();/***示意方法,实现某些功能的方法*/publicvoidsomeOperation(){//通常在这些方法实现中,需要调用工厂方法来获取Product对象Productproduct=factoryMethod();}}(4)再看看具体的创建器实现对象,示例代码如下:/***具体的创建器实现对象*/publicclassConcreteCreatorextendsCreator{protectedProductfactoryMethod(){//重定义工厂方法,返回一个具体的Product对象returnnewConcreteProduct();}}使用工厂方法模式来实现示例要使用工厂方法模式来实现示例,先来按照工厂方法模式的结构,对应出哪些是被创建的Product,哪些是Creator。分析要求实现的功能,导出的文件对象接口ExportFileApi就相当于是Product,而用来实现导出数据的业务功能对象就相当于Creator。把Product和Creator分开过后,就可以分别来实现它们了。使用工厂模式来实现示例的程序结构如图4所示:图4使用工厂模式来实现示例的程序结构示意图下面一起来看看代码实现。(1)导出的文件对象接口ExportFileApi的实现没有变化,这里就不去赘述了(2)接下来看看接口ExportFileApi的实现,为了示例简单,只实现导出文本文件格式和数据库备份文件两种。先看看导出文本文件格式的实现,示例代码如下:/***导出成文本文件格式的对象*/publicclassExportTxtFileimplementsExportFileApi{publicbooleanexport(Stringdata){//简单示意一下,这里需要操作文件System.out.println(导出数据+data+到文本文件);returntrue;}}再看看导出成数据库备份文件形式的对象的实现,示例代码如下:/***导出成数据库备份文件形式的对象*/publicclassExportDBimplementsExportFileApi{publicbooleanexport(Stringdata){//简单示意一下,这里需要操作数据库和文件System.out.println(导出数据+data+到数据库备份文件);returntrue;}}(3)Creator这边的实现,首先看看ExportOperate的实现,示例代码如下:/***实现导出数据的业务功能对象*/publicabstractclassExportOperate{/***导出文件*@paramdata需要保存的数据*@return是否成功导出文件*/publicbooleanexport(Stringdata){//使用工厂方法ExportFileApiapi=factoryMethod();returnapi.export(data);}/***工厂方法,创建导出的文件对象的接口对象*@return导出的文件对象的接口对象*/protectedabstractExportFileApifactoryMethod();}(4)加入了两个Creator实现,先看看创建导出成文本文件格式的对象,示例代码如下:/***具体的创建器实现对象,实现创建导出成文本文件格式的对象*/publicclassExportTxtFileOperateextendsExportOperate{protectedExportFileApifactoryMethod(){//创建导出成文本文件格式的对象returnnewExportTxtFile();}}再看看创建导出成数据库备份文件形式的对象,示例代码如下:/***具体的创建器实现对象,实现创建导出成数据库备份文件形式的对象*/publicclassExportDBOperateextendsExportOperate{protectedExportFileApifactoryMethod(){//创建导出成数据库备份文件形式的对象returnnewExportDB();}}(5)客户端直接创建需要使用的Creator对象,然后调用相应的功能方法,示例代码如下:publicclassClient{publicstaticvoidmain(String[]args){//创建需要使用的Creator对象ExportOperateoperate=newExportDBOperate();//调用输出数据的功能方法operate.export(测试数据);}}运行结果如下:导出数据测试数据到数据库备份文件单态模式定义:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。作用:性能优化应用场景1.1读取配置文件的内容考虑这样一个应用,读取配置文件的内容。很多应用项目,都有与应用相关的配置文件,这些配置文件多是由项目开发人员自定义的,在里面定义一些应用需要的参数数据。当然在实际的项目中,这种配置文件多采用xml格式的。也有采用properties格式的,毕竟使用Java来读取properties格式的配置文件比较简单。现在要读取配置文件的内容,该如何实现呢?1.2不用模式的解决方案有些朋友会想,要读取配置文件的内容,这也不是个什么困难的事情,直接读取文件的内容,然后把文件内容存放在相应的数据对象里面就可以了。真的这么简单吗?先实现看看吧。为了示例简单,假设系统是采用的properties格式的配置文件。(1)那么直接使用Java来读取配置文件,示例代码如下:Java代码/***读取应用配置文件*/publicclassAppConfig{/***用来存放配置文件中参数A的值*/privateStringparameterA;/***用来存放配置文件中参数B的值*/privateStringparameterB;publicStringgetParameterA(){returnparameterA;}publicStringgetParameterB(){returnparameterB;}/***构造方法*/publicAppConfig(){//调用读取配置文件的方法readConfig();}/***读取配置文件,把配置文件中的内容读出来设置到属性上*/privatevoidreadConfig(){Propertiesp=newProperties();InputStreamin=null;try{in=AppConfig.class.getResourceAsStream(AppConfig.properties);p.load(in);//把配置文件中的内容读出来设置到属性上this.parameterA=p.getProperty(paramA);this.parameterB=p.getProperty(paramB);}catch(IOExceptione){System.out.println(装载配置文件出错了,具体堆栈信息如下:);e.printStackTrace();}finally{try{in.close();}catch(IOExceptione){e.printStackTrace();}}}}注意:只有访问参数的方法,没有设置参数的方法。(2)应用的配置文件,名字是AppConfig.properties,放在AppConfig相同的包里面,简单示例如下:Java代码paramA=aparamB=b(3)写个客户端来测试一下,示例代码如下:Java代码publicclassClient{publicstaticvoidmain(String[]args){//创建读取应用配置的对象AppConfigconfig=newAppConfig();StringparamA=config.getParameterA();StringparamB=config.getParameterB();System.out.println(paramA=+paramA+,paramB=+paramB);}}运行结果如下:Java代码paramA=a,paramB=b1.3有何问题上面的实现很简单嘛,很容易的就实现了要求的功能。仔细想想,有没有什么问题呢?看看客户端使用这个类的地方,是通过new一个AppConfig的实例来得到一个操作配置文件内容的对象。如果在系统运行中,有很多地方都需要使用配置文件的内容,也就是很多地方都需要创建AppConfig这个对象的实例。换句话说,在系统运行期间,系统中会存在很多个AppConfig的实例对象,这有什么问题吗?当然有问题了,试想一下,每一个AppConfig实例对象,里面都封装着配置文件的内容,系统中有多个AppConfig实例对象,也就是说系统中会同时存在多份配置文件的内容,这会严重浪费内存资源。如果配置文件内容较少,问题还小一点,如果配置文件内容本来就多的话,对于系统资源的浪费问题就大了。事实上,对于AppConfig这种类,在运行期间,只需