简单工厂
简单工厂的定义
应用简单工厂来解决问题的思路
分析上面的问题,虽然不能让模块外部知道模块内部的具体实现,但是模块内部是可以知道实现类的,而且创建接口时需要具体实现类的。
那么,干脆在模块内部新建一个类,在这个类里面来创建接口,然后把创建的接口返回给客户端,这样,外部应用就只需要根据这个类来获取相应的接口对象,然后就可以操作接口定义的方法了。把这样的对象成为简单工厂,就叫它 Factory吧。
这样一来,客户端就可以通过Factoy 来获取简单的接口对象,然后调用接口的方法来实现需要的功能,而且客户端也不用再关心具体的实现了。
简单工厂的模板写法
(1) Api 定义的示例代码
/** | |
* 接口的定义,该接口可以通过简单工厂来创建 | |
*/ | |
public interface Api { | |
/** | |
* 具体功能方法的定义 | |
* @param s | |
*/ | |
public void operation(String s); | |
} |
(2) 接口实现A 代码
/** | |
* 接口的具体实现对象A | |
*/ | |
public class ImplA implements Api{ | |
public void operation(String s) { | |
//实现功能的代码 | |
System.out.println("Impl A s=="+s); | |
} | |
} |
(2) 接口实现B
/** | |
* 接口的具体实现对象B | |
*/ | |
public class ImplB implements Api{ | |
public void operation(String s) { | |
//实现功能的代码 | |
System.out.println("Impl B s=="+s); | |
} | |
} |
(4) 工厂类
/** | |
* 工厂类,用来创建Api对象 | |
*/ | |
public class Factory { | |
/** | |
* 具体创建Api对象的方法 | |
* @param condition 从外部传入的选择条件 | |
* @return | |
*/ | |
public static Api createApi(int condition){ | |
// 应该根据某些条件去选择究竟创建哪一个具体的实现对象 | |
// 这些条件可以从外部传入,也可以从其他途径来获取 | |
// 如果只有一个实现,可以省略条件,因为没有选择的必要 | |
Api api = null; | |
if(condition ==){ | |
api = new ImplA(); | |
}else if(condition ==){ | |
api = new ImplB(); | |
} | |
return api; | |
} | |
} |
(5)测试类
public class Main { | |
public static void main(String[] args) { | |
// 通过简单工厂来获取接口对象 | |
Api api = Factory.createApi(); | |
api.operation("正在使用简单工厂"); | |
} | |
} |
简单工厂的实例
下面我们举一个解析配置文件的例子。
配置文件的后缀有多种(json xml yaml properties) ,选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser…) ,将存储在文件中的配置解析成内存对象 RuleConfig。
public class RuleConfigSource { | |
public RuleConfig load(String ruleConfigFilePath) throws Exception { | |
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); | |
IRuleConfigParser parser = null; | |
if("json".equals(ruleConfigFileExtension)){ | |
parser = new JsonRuleConfigParser(); | |
}else if("xml".equals(ruleConfigFileExtension)){ | |
parser = new XmlRuleConfigParser(); | |
}else if("yaml".equals(ruleConfigFileExtension)){ | |
parser = new YamlRuleConfigParser(); | |
}else{ | |
throw new Exception("Rule config file format is not support..."+ruleConfigFileExtension); | |
} | |
String configContent = ""; | |
RuleConfig ruleConfig = parser.parse(configContent); | |
return ruleConfig; | |
} | |
private String getFileExtension(String ruleConfigFilePath) { | |
//根据配置文件获取 文件的后缀 | |
return "json"; | |
} | |
} |
我们对上面代码进行重构,把 选择解析器的代码剥离出来
public class RuleConfigSource { | |
public RuleConfig load(String ruleConfigFilePath) throws Exception { | |
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); | |
IRuleConfigParser parser = createParser(ruleConfigFileExtension); | |
if(parser == null){ | |
throw new Exception("Rule config file format is not support..."+ruleConfigFileExtension); | |
} | |
String configContent = ""; | |
RuleConfig ruleConfig = parser.parse(configContent); | |
return ruleConfig; | |
} | |
private IRuleConfigParser createParser(String ruleConfigFileExtension){ | |
IRuleConfigParser parser = null; | |
if("json".equals(ruleConfigFileExtension)){ | |
parser = new JsonRuleConfigParser(); | |
}else if("xml".equals(ruleConfigFileExtension)){ | |
parser = new XmlRuleConfigParser(); | |
}else if("yaml".equals(ruleConfigFileExtension)){ | |
parser = new YamlRuleConfigParser(); | |
} | |
return parser; | |
} | |
private String getFileExtension(String ruleConfigFilePath) { | |
//根据配置文件获取 文件的后缀 | |
return "json"; | |
} | |
} |
为了让类的职责更加单一、代码更加清晰,我们可以将 createParser 方法抽取到一个单独类中。
public class RuleConfigSource { | |
public RuleConfig load(String ruleConfigFilePath) throws Exception { | |
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); | |
IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension); | |
if(parser == null){ | |
throw new Exception("Rule config file format is not support..."+ruleConfigFileExtension); | |
} | |
String configContent = ""; | |
RuleConfig ruleConfig = parser.parse(configContent); | |
return ruleConfig; | |
} | |
private String getFileExtension(String ruleConfigFilePath) { | |
//根据配置文件获取 文件的后缀 | |
return "json"; | |
} | |
} | |
public class RuleConfigParserFactory { | |
public static IRuleConfigParser createParser(String ruleConfigFileExtension){ | |
IRuleConfigParser parser = null; | |
if("json".equals(ruleConfigFileExtension)){ | |
parser = new JsonRuleConfigParser(); | |
}else if("xml".equals(ruleConfigFileExtension)){ | |
parser = new XmlRuleConfigParser(); | |
}else if("yaml".equals(ruleConfigFileExtension)){ | |
parser = new YamlRuleConfigParser(); | |
} | |
return parser; | |
} | |
} |
上面的代码中,我们每次调用 RuleConfigParserFactory 的createParser() 的时候,都要创建一个新的parser 。实际上,如果parser 可以复用,为了节省内存,我们可以在系统启动的时候就初始化好,当调用createParser()函数的时候,我们可以从缓存中获取parser直接使用;
public class RuleConfigParserFactory { | |
private static final Map<String,IRuleConfigParser> 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 ruleConfigFileExtension){ | |
return cachedParsers.get(ruleConfigFileExtension); | |
} | |
} |
我们也可以提供注册接口,可以让用户自定义配置,然后添加到Map中;
public static void register(String ruleConfigFileExtension,IRuleConfigParser iRuleConfigParser ) | |
{ | |
cachedParsers.put(ruleConfigFileExtension,iRuleConfigParser); | |
} |
在Mybatis中注册TypeHandler也是使用这种模式
public final class TypeHandlerRegistry { | |
private final Map<JdbcType, TypeHandler >> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); | |
private final Map<Type, Map<JdbcType, TypeHandler >>> typeHandlerMap = new ConcurrentHashMap<>(); | |
private final TypeHandler<Object> unknownTypeHandler = new UnknownTypeHandler(this); | |
private final Map<Class >, TypeHandler >> allTypeHandlersMap = new HashMap<>(); | |
private static final Map<JdbcType, TypeHandler >> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); | |
private Class extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class; | |
public TypeHandlerRegistry() { | |
register(Boolean.class, new BooleanTypeHandler()); | |
register(boolean.class, new BooleanTypeHandler()); | |
register(JdbcType.BOOLEAN, new BooleanTypeHandler()); | |
register(JdbcType.BIT, new BooleanTypeHandler()); | |
register(Byte.class, new ByteTypeHandler()); | |
register(byte.class, new ByteTypeHandler()); | |
register(JdbcType.TINYINT, new ByteTypeHandler()); | |
...... | |
} | |
// java type + handler | |
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) { | |
register((Type) javaType, typeHandler); | |
} | |
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) { | |
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class); | |
if (mappedJdbcTypes != null) { | |
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) { | |
register(javaType, handledJdbcType, typeHandler); | |
} | |
if (mappedJdbcTypes.includeNullJdbcType()) { | |
register(javaType, null, typeHandler); | |
} | |
} else { | |
register(javaType, null, typeHandler); | |
} | |
} | |
} |
对于上面两种简单工厂模式的实现方法,如果我们要添加新的 parser,那势必要改动到 RuleConfigParserFactory 的代码,那这是不是违反开闭原则呢?实际上,如果不是需要频繁地添加新的 parser,只是偶尔修改一下 RuleConfigParserFactory 代码,稍微不符合开闭原则,也是完全可以接受的。
除此之外,在 RuleConfigParserFactory 的第一种代码实现中,有一组 if 分支判断逻辑,是不是应该用多态或其他设计模式来替代呢?实际上,如果 if 分支并不是很多,代码中有 if 分支也是完全可以接受的。应用多态或设计模式来替代 if 分支判断逻辑,也并不是没有任何缺点的,它虽然提高了代码的扩展性,更加符合开闭原则,但也增加了类的个数,牺牲了代码的可读性。
下面我们是使用注解来消灭if else
首先我们自定义一个注解:
@Target(ElementType.TYPE) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Documented | |
public @interface ConfChoice { | |
String confType(); | |
} |
每个解析类上加上注解
"json") | (confType =|
public class JsonRuleConfigParser implements IRuleConfigParser { | |
public RuleConfig parse(String text) { | |
return null; | |
} | |
} |
工厂解析类:
public class RuleConfigParserFactory { | |
private static final RuleConfigParserFactory INSTANCE = new RuleConfigParserFactory(); | |
private RuleConfigParserFactory(){} | |
public static RuleConfigParserFactory getInstance(){ | |
return INSTANCE; | |
} | |
private static final Map<String,String> cachedParsers = new HashMap<>(); | |
static{ | |
//、扫描支付渠道的实现类 ,Reflections 依赖 Google 的 Guava 库和 Javassist 库 | |
Reflections reflections = new Reflections("com.design.factory.simple"); | |
//、获取所有包含PayChannel注解的类 | |
Set<Class<?>> classList = reflections.getTypesAnnotatedWith(ConfChoice.class); | |
for (Class clazz : classList) { | |
ConfChoice type = (ConfChoice) clazz.getAnnotation(ConfChoice.class); | |
//、赋值payChannelMap,存储所有的支付渠道 | |
cachedParsers.put(type.confType(), clazz.getCanonicalName()); | |
} | |
} | |
public static IRuleConfigParser createParser(String ruleConfigFileExtension) throws ClassNotFoundException, IllegalAccessException, InstantiationException { | |
String className = cachedParsers.get(ruleConfigFileExtension); | |
Class clazz = Class.forName(className); | |
return (IRuleConfigParser)clazz.newInstance(); | |
} | |
} |
解析类的使用:
public class RuleConfigSource { | |
public RuleConfig load(String ruleConfigFilePath) throws Exception { | |
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); | |
IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension); | |
if(parser == null){ | |
throw new Exception("Rule config file format is not support..."+ruleConfigFileExtension); | |
} | |
String configContent = ""; | |
RuleConfig ruleConfig = parser.parse(configContent); | |
return ruleConfig; | |
} | |
private String getFileExtension(String ruleConfigFilePath) { | |
//根据配置文件获取 文件的后缀 | |
return "json"; | |
} | |
} |
何时选用简单工厂
建议在以下情况选用简单工厂
- 如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取相应的接口,而无须关心具体的实现。
- 如果想要把外部创建对象的职责几种管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责几种到一个简单工厂来,从而实现集中管理和控制。
工厂方法
工厂方法的定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory method 使一个类的实例化延迟到其子类
应用工厂方法模式来解决问题的思路
工厂方法模式的解决思路很有意思,那就是不解决,采取无为而治的方式: 不是需要一个接口对象吗?那就定义一个方法来创建;可是事实上他自己是不知道如何创建这个接口对象的,没有关系,定义成抽象对象本身就可以只是面向接口编程,而无需关心到底如何创建接口对象了。
工厂方法的模板写法
(1)Product 定义的示例代码如下
/** | |
* 工厂方法所创建的对象的接口 | |
*/ | |
public interface Product { | |
// 可以定义Product的属性和方法 | |
} |
(2)Product 实现对象的示例代码如下
/** | |
* 具体的Product对象 | |
*/ | |
public class ConcreteProduct implements Product{ | |
} |
(3)创建器定义的示例代码如下
/** | |
* 创建器,声明工厂方法 | |
*/ | |
public abstract class Creator { | |
protected abstract Product factoryMethod(); | |
public void someOperation(){ | |
//通常在这些方法实现中需要调用工厂方法来获取 Product 对象 | |
Product product = factoryMethod(); | |
} | |
} |
(4)创建器实现对象的示例代码如下
/** | |
* 具体的创建器的实现对象 | |
*/ | |
public class ConcreteCreator extends Creator{ | |
@Override | |
protected Product factoryMethod() { | |
//重定义工厂方法,返回一个具体的Product对象 | |
return new ConcreteProduct(); | |
} | |
} |
工厂方法的示例
我们使用工厂方法来修改上面的 解析配置文件解析器
public abstract class AbstractRuleConfigParserCreator { | |
abstract IRuleConfigParser createParser(); | |
public RuleConfig parse(String configText){ | |
IRuleConfigParser ruleConfigParser = createParser(); | |
return ruleConfigParser.parse(configText); | |
} | |
} | |
public class JsonRuleConfigParserCreator extends AbstractRuleConfigParserCreator{ | |
IRuleConfigParser createParser() { | |
return new JsonRuleConfigParser(); | |
} | |
} | |
public class XmlRuleConfigParserCreator extends AbstractRuleConfigParserCreator{ | |
IRuleConfigParser createParser() { | |
return new XmlRuleConfigParser(); | |
} | |
} | |
public class YamlRuleConfigParserCreator extends AbstractRuleConfigParserCreator{ | |
IRuleConfigParser createParser() { | |
return new YamlRuleConfigParser(); | |
} | |
} | |
public class PropertiesRuleConfigParserCreator extends AbstractRuleConfigParserCreator{ | |
IRuleConfigParser createParser() { | |
return new PropertiesRuleConfigParser(); | |
} | |
} |
那么如何将这些工厂类实现RuleConfigSource 的load()函数。具体代码如下:
public class RuleConfigSource { | |
public RuleConfig load(String ruleConfigFilePath) throws Exception { | |
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); | |
AbstractRuleConfigParserCreator parserCreator = null; | |
if("json".equals(ruleConfigFileExtension)){ | |
parserCreator = new JsonRuleConfigParserCreator(); | |
}else if("xml".equals(ruleConfigFileExtension)){ | |
parserCreator = new XmlRuleConfigParserCreator(); | |
}else if("yaml".equals(ruleConfigFileExtension)){ | |
parserCreator = new YamlRuleConfigParserCreator(); | |
}else if("properties".equals(ruleConfigFileExtension)){ | |
parserCreator = new PropertiesRuleConfigParserCreator(); | |
}else{ | |
throw new Exception(""); | |
} | |
String configText = ""; | |
return parserCreator.parse(configText); | |
} | |
private String getFileExtension(String ruleConfigFilePath) { | |
//根据配置文件获取 文件的后缀 | |
return "json"; | |
} | |
} |
我们还可以学着简单工厂模式 使用Map来缓存所有的Creator 类;
public class RuleConfigParserCreatorFactory { | |
private static final Map<String,AbstractRuleConfigParserCreator> cacheCreators = new HashMap<>(); | |
static{ | |
cacheCreators.put("json",new JsonRuleConfigParserCreator()); | |
cacheCreators.put("xml",new XmlRuleConfigParserCreator()); | |
cacheCreators.put("yaml",new YamlRuleConfigParserCreator()); | |
cacheCreators.put("properties",new PropertiesRuleConfigParserCreator()); | |
} | |
public static AbstractRuleConfigParserCreator getParserFactory(String type){ | |
if(type==null || type.isEmpty()){ | |
return null; | |
} | |
AbstractRuleConfigParserCreator parserCreator = cacheCreators.get(type); | |
return parserCreator; | |
} | |
} |
当我们需要添加新的规则配置解析器的时候,我们只需要创建新的 parser 类和 parser factory 类,并且在 RuleConfigParserFactoryMap 类中,将新的 parser factory 对象添加到 cachedFactories 中即可。代码的改动非常少,基本上符合开闭原则。
何时选用工厂方法模式
建议在以下情况下选用工厂方法模式。
- 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
- 如果一个类本身就希望由他的子类来创建所需的对象的时候,应该使用工厂方法模式。