1 基本介绍
简单工厂方法模式由于每次引入新产品时,都需要通过传入参数的不同来创建不同的产品,这必定要修改工厂类的代码,违反了“开闭原则”
从而出现了——工厂方法模式,就是解决该问题的
工厂方法中,不再提供统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂。
一般是定义一个用于创建对象的接口,让子类决定将哪一个类实例化。如下图
2 设计分析
2.1 简单工厂设计
通过栗子来理解把,现在某公司要设计一个系统运行日志记录器,用户可以通过修改配置文件更改日志记录方式,那么如何设计记录器的初始化和更改日志记录方式将会是一大难点。使用简单工厂模式设计如下
这时候,LoggerFactory代码大概为这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class LoggerFactory { public static Logger createLogger( string args){ if(args.equalsIgnoreCase ( "db")) { Logger logger : new DatabaseLogger( ); return logger; } else if(args.equalsignorecase( "file" )) { Logger logger = new FileLogger ( ); return logger; } else { return null; } } }
|
这样设计问题很大,包含大量的if...else...
代码,维护和测试难度增大,并且系统扩展不灵活
2.2 工厂方法设计
使用工厂方法设计日志记录器,将会变成下面这样
代码大概如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package DesignPatterns.JavaDesign.FactoryMethod;
public class Test { public static void main(String args[]) { LoggerFactory factory; Logger logger; factory = new FileLoggerFactory(); logger = factory.createLogger(); logger.writeLog( ); } }
interface Logger { public void writeLog(); }
class DatabaseLogger implements Logger { public void writeLog() { System.out.println("数据库日志记录。"); } }
class FileLogger implements Logger { public void writeLog() { System.out.println( "文件日志记录。"); } }
interface LoggerFactory { public Logger createLogger(); }
class DatabaseLoggerFactory implements LoggerFactory { public Logger createLogger() { Logger logger = new DatabaseLogger(); return logger; } }
class FileLoggerFactory implements LoggerFactory { public Logger createLogger() { Logger logger = new FileLogger( ); return logger; } }
|
添加xml配置文件优化
xml文件
1 2 3 4
| <?xml version="1.0"?> <config> <className>DesignPatterns.JavaDesign.FactoryMethod.DatabaseLoggerFactory</className> </config>
|
读取xml工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package DesignPatterns.JavaDesign.FactoryMethod;
import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File;
public class XMLUtil { public static Object getBean() { try { DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File(System.getProperty("user.dir") + "/src/main/resources/config.xml")); NodeList nl = doc.getElementsByTagName("className"); Node classNode = nl.item(0).getFirstChild(); String cName = classNode.getNodeValue(); Class<?> c = Class.forName(cName); Object obj = c.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } }
|
修改客户端代码
1 2 3 4 5 6 7 8 9 10 11
| public class Test { public static void main(String args[]) { LoggerFactory factory; Logger logger; factory = (LoggerFactory) XMLUtil.getBean(); logger = factory.createLogger(); logger.writeLog( ); } }
|
2.3 其他
重载的工厂方法
通过多种方式初始化日志记录器,例如可以提供默认实现,传入文件路径,或将参数封装在一个Object类型的对象中
工厂方法的隐藏
有时候需要对工厂方法函数进行隐藏
修改后的代码大致如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| abstract class LoggerFactory { public void writeLog () { Logger logger = this.createLogger( );logger.writeLog (); } public abstract Logger createLogger(); ... }
class client { public static void main( String args[]){ LoggerFactory factory; factory = (LoggerFactory )XMLUtil.getBean( ); factory.writeLog( ); } }
|
3 总结
优缺点分析
优点:
- 工厂方法用来创建客户所需的产品,同时隐藏了哪种具体产品类被实例化这一细节,用户只需关心产品对应的工厂,甚至都无需知道具体产品类的类名
- 加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,只需要添加一个具体工厂和具体产品就可以了。提高了系统的可扩展性,符合可扩展性
缺点:
- 添加新产品时需要添加对应工厂类和产品,类的个数成对增长,在一定程度上增加了系统复杂度
- 引入了抽象层,增加了系统的抽象和理解难度。且实现可能要用到DOM,反射技术,增加系统实现难度
使用场景
- 客户端不需要知道具体产品类的类名,只需要知道对应的工厂即可
- 抽象工厂类通过其子类来指定创建哪个对象
4 场景使用
4.1 Factory实例化Bean
FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,不要与容器BeanFacoty相混淆
FactoryBean,其主语是Bean,定语是Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过这里类型的Bean本身就是生产对象的工厂。
当使用该工厂Bean时需要实现三个方法
1 2 3 4 5 6 7 8 9 10 11 12
|
public String getObject() throws Exception;
public Class<?> getObjectType();
public boolean isSingleton();
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package DesignPatterns.JavaDesign.FactoryMethod;
import org.springframework.beans.factory.FactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); String bean = (String) context.getBean("bean"); System.out.println(bean); } }
class nameFactoryBean<T> implements FactoryBean<String> { @Override public String getObject() throws Exception { return new String("w1nd"); }
@Override public Class<?> getObjectType() { return null; }
@Override public boolean isSingleton() { return false; }
}
|
配置文件
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bean" class="DesignPatterns.JavaDesign.FactoryMethod.nameFactoryBean"></bean>
</beans>
|
参考
《Java设计模式》——刘伟
(108条消息) 创建对象与使用对象——谈谈工厂的作用_刘伟技术博客-CSDN博客
(109条消息) Spring 工厂方法与FactoryBean(实例化Bean)_浅然的专栏-CSDN博客_spring 工厂bean