【自分の総括】设计模式之工厂模式(实例分析)

来源:互联网 发布:sql server 百度网盘 编辑:程序博客网 时间:2024/04/30 02:08

一、概念

定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式使一个类的实例化延迟到子类。学习过抽象工厂模式会知道,抽象工厂模式中将产品角色抽象化,只提供了工厂类,该工厂类包含了创建具体产品类的所有逻辑,因此该类的职责较重,影响了系统的灵活性和扩展性。并且在抽象工厂模式中,倘若需要追加新的产品类到系统中,必须修改工厂类,这违背的开闭原则。而工厂模式中,将工厂角色也抽象化,客户端根据需要创建对应的工厂类,工厂类会创建用户所需的具体产品角色。也就是在工厂模式中,用户不用传参区分创建产品,而是区分所需的工厂类。

二、实例分析

背景引入,系统提供两个具体产品类分别对应两种日记记录方式(文件记录、数据库记录),两个具体产品类实现同一个抽象产品接口,对应两个具体产品类创建两个具体工厂类,两个具体工厂类实现同一个抽象工厂接口,具体工厂类中创建具体产品对象并返回。
1.抽象产品接口、抽象工厂接口

//abstract product interfacepublic interface Logger {public void writeLog();}
//abstract factory interfacepublic interface LoggerFactory {public Logger createLogger();}
2.两个具体产品类
//concrete product class No.1public class DatabaseLogger implements Logger {public void writeLog() {System.out.println("Database Logger working ...");}}
//concrete product class No.2public class FileLogger implements Logger {public void writeLog() {System.out.println("File Logger working ...");}}
3.两个具体工厂类
具体工厂类负责对应产品角色的创建及相关操作。

//concrete factory class No.1public class DatabaseLoggerFactory implements LoggerFactory {public Logger createLogger() {// connect DB and other code omittedLogger logger = new DatabaseLogger();return logger;}}
//concrete factory class No.2public class FileLoggerFactory implements LoggerFactory {public Logger createLogger() {// related operation code omittedLogger logger = new FileLogger();return logger;}}
4.用于测试的客户端类
在这里,客户端需要区分创建需要的工厂类,隐藏了哪种具体产品类被实例化这一细节。引入Java反射机制和配置文件读取两个技术了,客户端代码的使用将变得更加灵活。

public class Client {public static void main(String args[]) {LoggerFactory factory;Logger logger;// take file's operation as example// can be ralated method to make this become simplefactory = new FileLoggerFactory();logger = factory.createLogger();// other operation have similar effectlogger.writeLog(); //Display:File Logger working ...}}
三、Java反射机制和配置文件读取
在反射中使用最多的类是Class,Class类的实例表示正在运行的Java应用程序中的类和接口,其forName(String className)方法可以返回与带有给定字符串名的类或接口相关联的 Class对象,再通过Class对象的newInstance()方法创建此对象所表示的类的一个新实例,即通过一个类名字符串得到类的实例。如创建一个字符串类型的对象,其代码如下:

          Class c=Class.forName("String");           Object obj=c.newInstance();            return obj; 
原文提及的Class.forName使用时,传入类名称即。但是编者在实际运行时出现java.lang.ClassNotFoundException异常,传入的类名加上所在包名即可。如下:
          Class c=Class.forName("factory." + "String");           Object obj=c.newInstance();            return obj;  
如上,代码的灵活性是否受到影响?
软件系统的配置文件通常为XML文件,我们可以使用DOM (Document Object Model)、SAX (Simple API for XML)、StAX (Streaming API for XML)等技术来处理XML文件。
创建配置文件(config.xml)存储需要实例化的具体工厂类的名称:

<?xml version="1.0"?><config><className>DatabaseLoggerFactory</className></config>  
创建工具类(XMLUtil.java)用于读取配置文件中的内容、实例化并返回具体工厂类对象:
import javax.xml.parsers.*;import org.w3c.dom.*;import java.io.*;public class XMLUtil {// get target string from xmlfile and return to clientpublic static Object getBean() {try {// create document objectDocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;doc = builder.parse(new File("config.xml"));// get targetString's node// list be used yet get only one valueNodeList nl = doc.getElementsByTagName("className");Node classNode = nl.item(0).getFirstChild();String cName = classNode.getNodeValue();// instantiation object with class nameClass c = Class.forName("factory." + cName);Object obj = c.newInstance();return obj;} catch (Exception e) {e.printStackTrace();return null;}}}
修改客户端代码:
public class Client {public static void main(String args[]) {LoggerFactory factory;Logger logger;factory = (LoggerFactory) XMLUtil.getBean();logger = factory.createLogger();logger.writeLog();}}
四、总结

工厂方法模式的主要优点如下:
(1) 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
(2) 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
(3) 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
工厂方法模式的主要缺点如下:
(1) 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
(2) 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
在以下情况下可以考虑使用工厂方法模式:
(1) 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
(2) 抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

1 0