设计模式之工厂模式

设计模式之工厂模式

工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。
简单工厂模式

案例:根据不同文件类型,解析配置文件,源代码如下:

public RuleConfig load(String ruleConfigFilePath) {
    String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
    IRuleConfigParser parser = null;

    parser = createParserA(ruleConfigFileExtension);
    if (parser == null) {
        throw new InvalidRuleConfigException(
                "Rule config file format is not supported: " + ruleConfigFilePath);
    }

    //从ruleConfigFilePath文件中读取配置文本到configText中
    String configText = "";

    RuleConfig ruleConfig = parser.parse(configText);
    return ruleConfig;
}
private IRuleConfigParser createParserA(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;
}

为了让类的职责更加单一、代码更加清晰,我们还可以进一步将 createParser() 函数剥离到一个独立的类中,让这个类只负责对象的创建。而这个类就是我们现在要讲的简单工厂模式类 。

  • 实现方式1 通过调用类的静态方法创建解析类,也叫作简单工厂模式还叫作静态工厂方法模式(Static Factory Method Pattern)。之所以叫静态工厂方法模式,是因为其中创建对象的方法是静态的。 该方法创建的对象。
public class RuleConfigParserFactorySingleton {
    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;
    }
}
  • 实现方式2 每次调用 RuleConfigParserFactory 的 createParser() 的时候,都要创建一个新的 parser。如果 parser 可以复用,为了节省内存和对象创建的时间,可以将 parser 事先创建好缓存起来。当调用 createParser() 函数的时候,从缓存中取出 parser 对象直接使用。
public class RuleConfigParserFactoryPrototype {
    static Map<String, IRuleConfigParser> cachedRuleConfigParserMap = new HashMap<>();

    static {
        cachedRuleConfigParserMap.put("json", new JsonRuleConfigParser());
        cachedRuleConfigParserMap.put("xml", new XmlRuleConfigParser());
        cachedRuleConfigParserMap.put("yaml", new YamlRuleConfigParser());
        cachedRuleConfigParserMap.put("properties", new PropertiesRuleConfigParser());
    }

    public static IRuleConfigParser createParser(String configFormat) {
        if (configFormat == null || configFormat.isEmpty()) {
            return null;
        }

        return cachedRuleConfigParserMap.get(configFormat);
    }

    public static void main(String[] args) {
        IRuleConfigParser ruleConfigParser = RuleConfigParserFactoryPrototype.createParser("json");
        System.out.println(ruleConfigParser.getClass().getSimpleName());

        IRuleConfigParser ruleConfigParser2 = RuleConfigParserFactoryPrototype.createParser("doc");
        System.out.println(ruleConfigParser2.getClass().getSimpleName());
    }
}

尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加
parser,也没有太多的 parser)是没有问题的 。

工厂方法(Factory Method )

为了消除简单工厂模式实现方式1中的if…else,可以为每个文件创建一个工厂类,用于创建该解析类,如JsonRuleConfigParserFactory用于产生JsonRuleConfigParser类

package com.hero.designpatten.factory;
/**
 * @description: JsonRuleConfigParserFactory
 * @date: 2021/3/3 10:47
 * @author: hero
 * @version: 1.0
 */
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new JsonRuleConfigParser();
    }
}
package com.hero.designpatten.factory;

/**
 * @description: PropertiesRuleConfigParserFactory
 * @date: 2021/3/3 10:49
 * @author: hero
 * @version: 1.0
 */
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new PropertiesRuleConfigParser();
    }
}

该解决方案在使用时,仍然需要进行if…else进行判断,并未从根本解决该问题。

private IRuleConfigParser createParserB(String configFormat) {
    IRuleConfigParser parser = null;
    IRuleConfigParserFactory ruleConfigParserFactory = null;
    if ("json".equalsIgnoreCase(configFormat)) {
        ruleConfigParserFactory = new JsonRuleConfigParserFactory();
    } else if ("xml".equalsIgnoreCase(configFormat)) {
        ruleConfigParserFactory = new XmlRuleConfigParserFactory();
    } else if ("yaml".equalsIgnoreCase(configFormat)) {
        ruleConfigParserFactory = new YamlRuleConfigParserFactory();
    } else if ("properties".equalsIgnoreCase(configFormat)) {
        ruleConfigParserFactory = new PropertiesRuleConfigParserFactory();
    }

    //可能为空,异常暂不处理
    parser = ruleConfigParserFactory.createParser();
    return parser;
}
我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象

因为工厂类只包含方法,不包含成员变量,完全可以复用,
不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。

package com.hero.designpatten.factory;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: RuleConfigParserFactoryMap
 * @date: 2021/3/3 10:57
 * @author: hero
 * @version: 1.0
 */
public class RuleConfigParserFactoryMap {
    private static final Map<String, IRuleConfigParserFactory> CACHED_FACTORIES = new HashMap<>();
    static {
        CACHED_FACTORIES.put("json", new JsonRuleConfigParserFactory());
        CACHED_FACTORIES.put("xml", new XmlRuleConfigParserFactory());
        CACHED_FACTORIES.put("yaml", new YamlRuleConfigParserFactory());
        CACHED_FACTORIES.put("properties", new PropertiesRuleConfigParserFactory());
    }

    public static IRuleConfigParserFactory getParserFactory(String type) {
        if (type == null || type.isEmpty()) {
            return null;
        }
        return CACHED_FACTORIES.get(type.toLowerCase());
    }
}
private IRuleConfigParser createParserC(String configFormat) {
    IRuleConfigParser parser = null;
    IRuleConfigParserFactory ruleConfigParserFactory = null;

    ruleConfigParserFactory = RuleConfigParserFactoryMap.getParserFactory(configFormat);
    //可能为空,异常暂不处理
    parser = ruleConfigParserFactory.createParser();
    return parser;
}

当我们需要添加新的规则配置解析器的时候,我们只需要创建新的 parser 类和 parserfactory 类,并且在 RuleConfigParserFactoryMap 类中,将新的 parser factory 对象添加到 cachedFactories 中即可。代码的改动非常少,基本上符合开闭原则。

抽象工厂

抽象工厂模式的应用场景比较特殊,没有前两种常用。一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser对象。这样就可以有效地减少工厂类的个数。

使用场景

当创建逻辑比较复杂,是一个“大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。

第一种情况:类似规则配置解析的例子,代码中存在 if-else 分支判断,动态地根据不同的类型创建不同的对象。针对这种情况,我们就考虑使用工厂模式,将这一大坨 if-else创建对象的代码抽离出来,放到工厂类中。

第二种情况,尽管我们不需要根据不同的类型创建不同的对象,但是,单个对象本身的创建过程比较复杂,比如前面提到的要组合其他类对象,做各种初始化操作。在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中。

对于第一种情况,当每个对象的创建逻辑都比较简单的时候,推荐使用简单工厂模式,将多个对象的创建逻辑放到一个工厂类中。当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的简单工厂类,推荐使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。同理,对于第二种情况,因为单个对象本身的创建逻辑就比较复杂,所以,建议使用工厂方法模式

除了刚刚提到的这几种情况之外,如果创建对象的逻辑并不复杂,就直接通过 new来创建对象就可以了,不需要使用工厂模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值