简单工厂的严重问题:
当系统中需要引进新产品时,静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,违背了开闭原则
引入工厂方法模式:
针对不同的产品提供不同的工厂
定义:
定义一个用于创建对象的接口,让子类决定将 哪一个类实例化,工厂方法迷失让一个类实例化延迟到其子类,工厂方法模式又称为工厂模式(Factory Pattern),又课称作虚拟构造器模式(VIrtual Constructor Pattern)
一、工厂方法模式概述:
引入抽象工厂角色,可以是接口,也可以是抽象类或者具体类
代码:
interface Factory{
public Product factoryMethod();
}
class ConcreteFactory implements Factory{
public Product factoryMethod(){
return new COncreteFactory();
}
}
二、完整解决方案:
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 Factory{
public Logger createLogger(){
Logger logger =new DatabaseLogger();
return logger();
}
}
class FileLoggerFactory implements Factory{
public Logger createLogger(){
Logger logger=new FileLogger();
return logger;
}
}
class Client{
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
factory=new FileLoggerFactory();
logger=factory.createLogger();
logger.writeLog()
}
}
三、反射与配置文件
利用反射,返回xml中指定的对象,这样修改就不用修改源码了。
<?xml version="1.0"?>
<config>
<chartType>histogram</chartType>
</config>
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
class XMLUtil{
public static String getChartType(){
try{
DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
DocumentBuilder builer=dFactory.newDocumentBuilder();
Document doc;
doc=builer.parse(new File("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;
}
}
}
Client端修改如下:
class Client{
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
factory=(LoggerFactory)XMLUtil.getBean;
logger=factory.createLogger();
logger.writeLog()
}
四、重载的工厂方法
使用工厂创建对象时,创建方法可以重载,使用args参数,obj参数,默认方式等多种参数来创建工厂
class DatabaseLoggerFactory implements Factory{
public Logger createLogger(){
Logger logger =new DatabaseLogger();
return logger();
}
public Logger createLogger(String args){
Logger logger =new DatabaseLogger();
return logger();
}
public Logger createLogger(Object obj){
Logger logger =new DatabaseLogger();
return logger();
}
}
五、工厂方法模式总结:
- 主要优点:
- 用火狐只需关心产品对应的工厂,无需关心创建细节,甚至具体产品的类的类名
- 多态,所有具体工厂类都具有同一个父类
- 加入新产品时,无需修改抽象工厂和抽象产品听的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,而指套添加一个具体工厂和具体产品就可以了,扩展性好
- 主要缺点:
- 添加新产品时徐太编写新的具体产品类
- 为了扩展性,需要引入抽象层,增加系统的抽象性和理解难度,增加了实现的难度
- 适用场景:
- 客户端不知道其所需要的对象的类
- 抽象工厂类通过其子类来指定创建那个对象