简单工厂
简单工厂的定义
应用简单工厂来解决问题的思路
分析上面的问题,虽然不能让模块外部知道模块内部的具体实现,但是模块内部是可以知道实现类的,而且创建接口时需要具体实现类的。
那么,干脆在模块内部新建一个类,在这个类里面来创建接口,然后把创建的接口返回给客户端,这样,外部应用就只需要根据这个类来获取相应的接口对象,然后就可以操作接口定义的方法了。把这样的对象成为简单工厂,就叫它 Factory吧。
这样一来,客户端就可以通过Factoy 来获取简单的接口对象,然后调用接口的方法来实现需要的功能,而且客户端也不用再关心具体的实现了。
简单工厂的模板写法
(1) Api 定义的示例代码
/**
* 接口的定义,该接口可以通过简单工厂来创建
*/
public interface Api {
/**
* 具体功能方法的定义
* @param s
*/
public void operation(String s);
}
(2) 接口实现A 代码
/**
* 接口的具体实现对象A
*/
public class ImplA implements Api{
@Override
public void operation(String s) {
//实现功能的代码
System.out.println("Impl A s=="+s);
}
}
(2) 接口实现B
/**
* 接口的具体实现对象B
*/
public class ImplB implements Api{
@Override
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();
}
每个解析类上加上注解
@ConfChoice(confType = "json")
public class JsonRuleConfigParser implements IRuleConfigParser {
@Override
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{
@Override
IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}
public class XmlRuleConfigParserCreator extends AbstractRuleConfigParserCreator{
@Override
IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
public class YamlRuleConfigParserCreator extends AbstractRuleConfigParserCreator{
@Override
IRuleConfigParser createParser() {
return new YamlRuleConfigParser();
}
}
public class PropertiesRuleConfigParserCreator extends AbstractRuleConfigParserCreator{
@Override
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 中即可。代码的改动非常少,基本上符合开闭原则。
何时选用工厂方法模式
建议在以下情况下选用工厂方法模式。
- 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
- 如果一个类本身就希望由他的子类来创建所需的对象的时候,应该使用工厂方法模式。