工厂模式

类型:创建型设计模式

细分:简单工厂、工厂方法模式、抽象工厂模式

  • 简单工厂在需要频繁增删对象类型的情况下,需要频繁地修改创建对象的静态方法,违背开闭原则。
  • 工厂方法模式解决了简单工厂违背开闭原则的问题,在抽象类定义了要返回的抽象类型,将要返回的具体类型延迟到子类。
  • 抽象工厂模式在工厂模式的基础上,在抽象类中定义了多个工厂方法,可以减少工厂类的数量,并且切换不同的工厂实现类时,其返回的对象都会一并切换。
  • 对于简单工厂的 if 分支,可以通过约定或配置 + 反射来消除。

简单工厂和工厂方法模式的选择:

  • 如果 if 分支比较多,可以选择工厂方法模式来消除简单工厂的 if 分支
  • 如果创建逻辑比较复杂,比如需要组合其他对象或执行复杂的初始化,可以选择工厂方法模式,将复杂的创建逻辑封装到各个工厂类中
  • 对于简单的情况,使用简单工厂即可

解决的问题:

  • 如果一个抽象类型有多种不同的子类,则可以考虑使用工厂模式
  • 解耦对象的创建和使用
  • 封装复杂的创建过程,达到代码复用目的
  • 实现面向接口编程,客户端不会直接和具体类型耦合

抽象类型在设计模式中是一个比较广泛的概念,可以指 Java 中的接口和抽象类。

简单工厂模式

public class ConfigParserFactory {
    private static final String BAD_FILE_EXTENSION = "";
    // 工具类,将构造器设置成私有的
    private ConfigParserFactory() {}

    public static IConfigParser createParser(String path) {
        String fileExtension = getFileExtension(path);
        if ("json".equalsIgnoreCase(fileExtension)) {
            return new JsonConfigParser();
        } else if ("xml".equalsIgnoreCase(fileExtension)) {
            return new XmlConfigParser();
        } else if ("properties".equalsIgnoreCase(fileExtension)) {
            return new XmlConfigParser();
        } else {
            throw new RuntimeException("该文件类型不支持");
        }
    }

    private static String getFileExtension(String path){
        if (StringUtils.isBlank(path)) {
            return BAD_FILE_EXTENSION;
        }
        int index = path.lastIndexOf(".");
        if (index == -1) {
            return BAD_FILE_EXTENSION;
        }
        return path.substring(index + 1);
    }
}

命名习惯:

  • 类名:XXXFactory
  • 方法名:createXXX(), createInstance(), getInstance(), newInstance()

在对象可以复用的情况,可以将对象缓存起来:

public class CachedConfigParserFactory {
    private static final String BAD_FILE_EXTENSION = "";
    private static final Map<String, IConfigParser> parsers = new HashMap<>();

    static {
        parsers.put("json", new JsonConfigParser());
        parsers.put("xml", new XmlConfigParser());
        parsers.put("properties", new PropertiesConfigParser());
    }

    // 工具类,构造器设置成私有的
    private CachedConfigParserFactory() {}

    public static IConfigParser createParser(String path) {
        String fileExtension = getFileExtension(path);
        if (fileExtension == BAD_FILE_EXTENSION || !parsers.containsKey(fileExtension)) {
            throw new RuntimeException("文件类型不支持");
        }
        return parsers.get(fileExtension);
    }

    private static String getFileExtension(String path){
        if (StringUtils.isBlank(path)) {
            return BAD_FILE_EXTENSION;
        }
        int index = path.lastIndexOf(".");
        if (index == -1) {
            return BAD_FILE_EXTENSION;
        }
        return path.substring(index + 1);
    }
}

如果需要频繁地增删解析器,那么就需要频繁地修改 createConfigParser() 方法,违反了开闭原则。

这种情况可以用工厂方法模式解决。

工厂方法模式

在工厂方法模式中,新增解析器只需要增加一个工厂类即可。

public interface IConfigParserFactory {
    IConfigParser createParser();
}
public class JsonConfigParserFactory implements IConfigParserFactory {
    @Override
    public IConfigParser createParser() {
        return new JsonConfigParser();
    }
}

public class XmlConfigParserFactory implements IConfigParserFactory {
    @Override
    public IConfigParser createParser() {
        return new XmlConfigParser();
    }
}

public class PropertiesConfigParserFactory implements IConfigParserFactory {
    @Override
    public IConfigParser createParser() {
        return new PropertiesConfigParser();
    }
}

我们可以新增一个配置解析器工厂的简单工厂来封装根据文件扩展名来获取解析器工厂的逻辑:

/**
 * 配置解析器工厂的工厂
 * */
public class ConfigParserFactories {
    private static final String BAD_FILE_EXTENSION = "";
    private static final Map<String, IConfigParserFactory> factories = new HashMap<>();
    
    static {
        factories.put("json", new JsonConfigParserFactory());
        factories.put("xml", new XmlConfigParserFactory());
        factories.put("properties", new PropertiesConfigParserFactory());
    }
    
    // 工具类,将构造器设置成私有的
    private ConfigParserFactories() {}

    public static IConfigParserFactory getFactory(String path) {
        String fileExtension = getFileExtension(path);
        if (fileExtension == BAD_FILE_EXTENSION || !factories.containsKey(fileExtension)) {
            throw new RuntimeException("文件类型不支持");
        }
        return factories.get(fileExtension);
    }
    
    private static String getFileExtension(String path){
        if (StringUtils.isBlank(path)) {
            return BAD_FILE_EXTENSION;
        }
        int index = path.lastIndexOf(".");
        if (index == -1) {
            return BAD_FILE_EXTENSION;
        }
        return path.substring(index + 1);
    }
}

抽象工厂模式

抽象工厂模式,就是一个工厂类可以有多个对应的工厂方法。

public interface IFactory {
    Instance1 createInstance1();
    Instance2 createInstance2();
}

参阅

  • 设计模式之美 - 王争
posted @ 2022-08-04 22:32  廖子博  阅读(32)  评论(0编辑  收藏  举报