深入理解java工厂设计模式

Java
219
0
0
2023-10-16

简单工厂

简单工厂的定义

应用简单工厂来解决问题的思路

分析上面的问题,虽然不能让模块外部知道模块内部的具体实现,但是模块内部是可以知道实现类的,而且创建接口时需要具体实现类的。

那么,干脆在模块内部新建一个类,在这个类里面来创建接口,然后把创建的接口返回给客户端,这样,外部应用就只需要根据这个类来获取相应的接口对象,然后就可以操作接口定义的方法了。把这样的对象成为简单工厂,就叫它 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 中即可。代码的改动非常少,基本上符合开闭原则。

何时选用工厂方法模式

建议在以下情况下选用工厂方法模式。

  • 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
  • 如果一个类本身就希望由他的子类来创建所需的对象的时候,应该使用工厂方法模式。