一般情况下,工厂模式分为 三种
- 简单工厂
- 工厂方法
- 抽象工厂
在我看来,简单工厂其实是工厂方法的一种特例,但它在我们项目开发中基本上必须使用,单独抽出来。
工厂方法 在spring、mybatis等源码中基本上都存在 ,项目中也会使用,也是必须掌握的,抽象工厂相对用的比较少,这里就不多说了,感兴趣的可以自行研究
简单工厂
先演示代码,基本两种情况,第一种每次new一个对象,第二种缓存对象,直接获取
// 第一种
public class RuleConfigParserFactory {
public static IRuleConfigParser createParser(String configFormat) {
IRuleConfigParser parser = null;
if ("json".equalsIgnoreCase(configFormat)) {
parser = new JsonRuleConfigParser();
} else if ("xml".equalsIgnoreCase(configFormat)) {
parser = new XmlRuleConfigParser();
} else if ("yaml".equalsIgnoreCase(configFormat)) {
parser = new YamlRuleConfigParser();
} else if ("properties".equalsIgnoreCase(configFormat)) {
parser = new PropertiesRuleConfigParser();
}
return parser;
}
}
//第二种
public class RuleConfigParserFactory {
private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParser());
cachedParsers.put("xml", new XmlRuleConfigParser());
cachedParsers.put("yaml", new YamlRuleConfigParser());
cachedParsers.put("properties", new PropertiesRuleConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;//返回null还是IllegalArgumentException全凭你自己说了算
}
IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
return parser;
}
}
对于这两种情况,如果我们要添加新的parser,只有要修改下 代码,稍微不符合开闭原则,
对应 if 判断,如果代码比较简单,也没什么问题,可读性、可修改都很灵活
工厂方法
那么工厂方法和简单工厂区别是什么呢, 其实我们推演下,把刚才的if 分支去掉,使用多态的方式替代掉
这里区别是什么
重点是对象的创建是否复杂, 对应创建型的设计模式,主要是用来创建对象的,
- 如果对象的创建不复杂,比如new 就可以了,我们只有使用简单工厂就可以了,
- 如果对象创建比较复杂,涉及引用其他对象,
- 或者判断逻辑,我们把对象的创建拆分开,单独一个类,隐藏创建的复杂逻辑,这里就可以使用工厂方法了
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser();
}
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new YamlRuleConfigParser();
}
}
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new PropertiesRuleConfigParser();
}
}
这个怎么使用呢,这里又绕回去了,我们需要再创建一个类似简单工厂的形式 ,它复杂创建工厂的工厂
//因为工厂类只包含方法,不包含成员变量,完全可以复用,
//不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。
public class RuleConfigParserFactoryMap { //工厂的工厂
private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>();
static {
cachedFactories.put("json", new JsonRuleConfigParserFactory());
cachedFactories.put("xml", new XmlRuleConfigParserFactory());
cachedFactories.put("yaml", new YamlRuleConfigParserFactory());
cachedFactories.put("properties", new PropertiesRuleConfigParserFactory());
}
public static IRuleConfigParserFactory getParserFactory(String type) {
if (type == null || type.isEmpty()) {
return null;
}
IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase());
return parserFactory;
}
}
对应我们项目中,哪些会用到工厂方法呢
- Java 中的 Calendar、DateFormat 类。
- valueOf() 返回与入参相等的对象 例如 Integer.valueOf()
- getInstance() 返回单例对象 例如 Calendar.getInstance()
- newInstance() 每次调用时返回新的对象 例如 HelloWorld.class.getConstructor().newInstance()
- 在反射中的工厂方法 例如 XXX.class.getField(String name) 返回成员
抽象工厂
这里简单说一下,比较抽象工厂我们不常使用,比如在代码实例中,规则配置解析,会根据 josn、xml、yaml等分类,创建不同的工厂,但是如果类有2种划分方式,比如我们 按照配置文件格式分类,也按照解析对象是rule配置还是系统配置分类,就会有8种方式,产生类爆炸
针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser
针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser
怎么解决呢 我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象
public interface IConfigParserFactory {
IRuleConfigParser createRuleParser();
ISystemConfigParser createSystemParser();
//此处可以扩展新的parser类型,比如IBizConfigParser
}
public class JsonConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleParser() {
return new JsonRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemParser() {
return new JsonSystemConfigParser();
}
}
public class XmlConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleParser() {
return new XmlRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemParser() {
return new XmlSystemConfigParser();
}
}
// 省略YamlConfigParserFactory和PropertiesConfigParserFactory代码
这个可以看出了,确实不太常用
工厂方法的改良
实际上 ,对于简单工厂,我们代码中还会经常使用,
对于工厂方法,传统的还是太麻烦了, 现在我们利用 spring容器,可以这样使用
一、利用 ioc注入
将不同的RuleConfigParser实现按照约定格式指定beanName注入,
比方说@Component(“XmlRuleConfigParser”),取的时候applicationContext.getBean(typeSuffix+RuleConfigParser)即可,拓展的话,自己写一个xxRuleConfigParser,就注入进去了,也不需要在map容器新增。 整个工厂方法就是
public RuleConfigParser getInstance(suffix){
return InstanceLocator.getBean(suffix+"RuleConfigParser");
}
二、利用反射
下面是我借助于chatgpt 生成的代码
可以将工厂类的配置信息存储在属性文件中,然后使用Java反射机制动态创建工厂对象。具体实现步骤如下:
可以将工厂类的配置信息存储在属性文件中,然后使用Java反射机制动态创建工厂对象。具体实现步骤如下:
- 创建配置文件factory.properties,用于存储工厂类的配置信息,格式如下所示:
parser.factory.json=com.example.factory.JsonRuleConfigParserFactory
parser.factory.xml=com.example.factory.XmlRuleConfigParserFactory
parser.factory.yaml=com.example.factory.YamlRuleConfigParserFactory
parser.factory.properties=com.example.factory.PropertiesRuleConfigParserFactory
其中,parser.factory.json表示对应的解析器类型,com.example.factory.JsonRuleConfigParserFactory表示该类型对应的工厂类的全限定名称。
- 定义一个工厂类RuleConfigParserFactory,该类读取配置文件中的信息,并使用反射机制动态创建相应的工厂对象,代码如下所示:
public class RuleConfigParserFactory {
private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>();
static {
Properties props = new Properties();
InputStream in = null;
in = RuleConfigParserFactory.class.getResourceAsStream("/factory.properties");
props.load(in);
for (Object name : props.keySet()) {
String type = (String) name;
String className = props.getProperty(type);
Class<?> clazz = Class.forName(className);
IRuleConfigParserFactory factory = (IRuleConfigParserFactory) clazz.newInstance();
cachedFactories.put(type, factory);
}
}
public static IRuleConfigParserFactory getParserFactory(String type) {
if (type == null || type.isEmpty()) {
return null;
}
IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase());
return parserFactory;
}
}
该类通过读取配置文件,在静态代码块中反射创建工厂对象,并将其缓存起来。
- 客户端调用工厂的getParserFactory(type)方法来获取相应类型的规则配置解析器对象。例如:
IRuleConfigParserFactory factory = RuleConfigParserFactory.getParserFactory("json");
IRuleConfigParser parser = factory.createParser();
该代码可以根据解析器类型来创建相应类型的工厂对象,并进一步使用工厂对象创建解析器对象。
使用反射机制来实现工厂的获取可以提高代码的灵活性和可维护性,并且避免了手动创建工厂对象的麻烦。