设计模式系列-------工厂模式和抽象工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工厂方法原理很简单,通过把创建对象的逻辑抽离出来,并对外提供一个工厂类的方式,让类的职责更加单一,代码更加清晰。使用者不需要关心具体对象初始化细节,只需要知道传入工厂类的参数。工厂方式一般可以分为三种:简单工厂模式,工厂模式,抽象工厂模式。一般文章或者书会把简单工厂模式和工厂模式当作一种设计模式,把抽象工厂模式当作另一种设计模式。本文通过一个解析配置文件的例子来演示不同工厂模式之间到底有什么区别。

内容总结:

案例介绍

简单工厂模式

工厂模式

工厂模式和简单工厂模式的选择

抽象工厂

三种工厂对比

开源代码中的工厂模式


 

案例介绍

现在有一个需求,我们根据配置文件的后缀(xml,json,yaml等),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将文件中的配置解析成配置对象RuleConfig。 下面提供了一中最直接的写法,在方法中直接根据文件的后缀,生成对应的解析器,然后将配置文件解析为RuleConfig对象。你可以发现,这种写法代码的可阅读性和拓展性都很差,包括中间一大段的if-else语句,以及如果想要添加新的后缀,那我就要改动对应的代码。

/**
* 生成对应的解释器跟业务代码耦合
*/
public class RuleConfigSource {
  public RuleConfig load(String ruleConfigFilePath) {
    String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
    IRuleConfigParser parser = null;
    if ("json".equalsIgnoreCase(ruleConfigFileExtension)) {
      parser = new JsonRuleConfigParser();
    } else if ("xml".equalsIgnoreCase(ruleConfigFileExtension)) {
      parser = new XmlRuleConfigParser();
    } else if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
      parser = new YamlRuleConfigParser();
    } else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
      parser = new PropertiesRuleConfigParser();
    } else {
      throw new InvalidRuleConfigException(
             "Rule config file format is not supported: " + ruleConfigFilePath);
    }

    String configText = "";
    //从ruleConfigFilePath文件中读取配置文本到configText中
    RuleConfig ruleConfig = parser.parse(configText);
    return ruleConfig;
  }

  private String getFileExtension(String filePath) {
    //...解析文件名获取扩展名,比如rule.json,返回json
    return "json";
  }
}

/**
* 以下是解析器对应的代码,具体ConfigParser如何解析文件,生成RuleConfig的功能省略
*/
public interface IRuleConfigParser {
  RuleConfig parse(String configText);
}

public class JsonRuleConfigParser implements IRuleConfigParser {
  @Override
  public RuleConfig parse(String configText) {
    return new RuleConfig ();
  }
}

public class XmlRuleConfigParser implements IRuleConfigParser {
  @Override
  public RuleConfig parse(String configText) {
    return new RuleConfig ();
  }
}

public class YamlRuleConfigParser implements IRuleConfigParser {
  @Override
  public RuleConfig parse(String configText) {
    return new RuleConfig ();
  }
}

public class PropertiesRuleConfigParser implements IRuleConfigParser {
  @Override
  public RuleConfig parse(String configText) {
    return new RuleConfig ();
  }
}

简单工厂模式

可能你会想到把中间的if-else重新封装成一个方法,这种思路跟简单工厂模式很像,只不过简单工厂模式做到更加彻底,他会将if-else部分的逻辑剥离到一个独立的类中,让这个类负责对象的创建。这种写法可以让类的职责更加单一,代码更加清晰。对于简单工厂模式来说,只有一个工厂类,如果需要新的解析器的时候,修改对应的工厂方法,就可以满足需求。适合用于功能变化不多的场景。


public class RuleConfigSource {
  public RuleConfig load(String ruleConfigFilePath) {
    String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
    //将对应的判断和生成对应的解析类转剥离到一个类中,通过静态方法调用
    IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
    if (parser == null) {
      throw new InvalidRuleConfigException(
              "Rule config file format is not supported: " + ruleConfigFilePath);
    }

    String configText = "";
    //从ruleConfigFilePath文件中读取配置文本到configText中
    RuleConfig ruleConfig = parser.parse(configText);
    return ruleConfig;
  }

  private String getFileExtension(String filePath) {
    //...解析文件名获取扩展名,比如rule.json,返回json
    return "json";
  }
}

/**
* 写法一:每次根据传入参数,生成新的对象返回
*/
public class RuleConfigParserFactory {
  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;
  }
}

/**
* 写法二:将要返回的对象先存入缓存中,然后根据传入参数直接返回,避免了生成和回收时的资源浪费
*/

public class RuleConfigParserFactory {
  private static final Map<String, RuleConfigParser> 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 configFormat) {
    if (configFormat == null || configFormat.isEmpty()) {
      return null;//返回null还是IllegalArgumentException全凭你自己说了算
    }
    IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
    return parser;
  }
}

工厂模式

工厂模式和简单工厂模式相差不大,这两者区别在于,工厂模式不只提供一个统一的工厂类(简单工厂模式中的RuleConfigParserFactory ),而是提供一个抽象的手机代工厂,并且针对不同的对象再提供不同的工厂类。当参数传入时,抽象工厂方法会根据传入的参数,选择对应的工厂类。然后通过对应的工厂类生成对应的对象。原来的工具类会被拆分成以下代码:

/**
* 针对不同的解释器,创建不同的工厂类。
*/
public interface IRuleConfigParserFactory {
  IRuleConfigParser createParser();
}

public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new JsonRuleConfigParser();
  }
}

public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new XmlRuleConfigParser();
  }
}

public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new YamlRuleConfigParser();
  }
}

public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new PropertiesRuleConfigParser();
  }
}

/**
* 简单工厂模式是直接new 对象返回,而现在修改为new 对应的工厂,
* 然后调用工厂方法返回对应的对象
*/
public class RuleConfigParserFactory {
  public static IRuleConfigParser createParser(String configFormat) {
    IRuleConfigParserFactory parserFactory = null;
    if ("json".equalsIgnoreCase(configFormat)) {
      parserFactory = new JsonRuleConfigParserFactory();
    } else if ("xml".equalsIgnoreCase(configFormat)) {
      parserFactory = new XmlRuleConfigParserFactory();
    } else if ("yaml".equalsIgnoreCase(configFormat)) {
      parserFactory = new YamlRuleConfigParserFactory();
    } else if ("properties".equalsIgnoreCase(configFormat)) {
      parserFactory = new PropertiesRuleConfigParserFactory();
    }
    return parserFactory.createParser;
  }
}

工厂模式和简单工厂模式的选择

你可能觉得简单工厂模式和工厂模式没什么区别,而且按照工厂模式的写法,功能上没什么变化,但是增加了很多工厂类,提高了设计的复杂度。那我们为什么要使用工厂模式呢?网上有些答案说简单工厂模式不利于拓展,每添加一个类都要修改工厂类,违背了开闭原则,或者说工厂模式可以让我们不需要知道具体对象类型,只要知道对应的工厂类就可以。

我觉得这些都不是主要原因。因为无论简单工厂模式还是工厂模式在新增一个类的时候,都要修改对应的工厂类或者抽象工厂类,把新增的对象或者工厂类添加进去。此外,这两种方式都隐藏了具体类的实现方式,只不过工厂模式隐藏的更深。我觉得选择工厂模式还是简单工厂模式的关键在于可创建对象的数量以及创建对象的复杂程度。在本文的例子中,可创建的类型不多(根据后缀,现在只有4种),而且创建的方式也很简单(只需要new就可创建对应的对象)。但是,如果我们要创建的对象种类有很多(假设有十几种),而且创建的方式很复杂(需要十几或者几十行代码),那么,当你选择使用简单工厂模式的时,你的工厂类就会很复杂,代码逻辑不清晰。而使用工厂模式,把对应复杂的对象创建的过程封装到对应的工厂类中。保证了代码的可读性,以及让类的职责更加单一。

所以,选择工厂模式还是简单工厂模式要根据业务场景。如果业务很简单(类似文中这样),那么简单工厂模式会比工厂模式更好理解和书写,如果业务很复杂,那么工厂模式会比简单工厂模式更好理解。

抽象工厂

抽象工厂可以理解为是工厂方法的拓展,在上面的例子,我们根据解析文件的类型,生成了四个工厂类,每个类中固定返回IRuleConfigParser类的对象。如果我们的需求变了,除了将文件解析成IRuleConfigParser类,我们还要支持将文件解析成另一种类ISystemConfigParser。如果我们将以前的做法,可以再新增四个工厂类,每个工厂类返回对应的ISystemConfigParser。

针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser

针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser

如果使用抽象工厂,可以通过对需求进行分析,将类似的功能汇总成一个新的抽象类,从而减少类的数量

/**
* 抽象工厂其实就是一个类中定义多个接口,然后所有的工厂类都实现对应的功能
*/
public interface IConfigParserFactory {
  IRuleConfigParser createRuleParser();
  ISystemConfigParser createSystemParser();
  //此处可以扩展新的parser类型,比如IBizConfigParser
}

public class JsonConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new JsonRuleConfigParser();
  }

  @Override
  public ISystemConfigParser createSystemParser() {
    return new JsonSystemConfigParser();
  }
}

public class XmlConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new XmlRuleConfigParser();
  }

  @Override
  public ISystemConfigParser createSystemParser() {
    return new XmlSystemConfigParser();
  }
}

// 省略YamlConfigParserFactory和PropertiesConfigParserFactory代码

个人理解抽象工厂是对相当于对工厂模式进行归纳汇总,从而减少创建的类。但是如果出现了一种新的,但是不在我们之前归纳方式之内的类,那么抽象工厂就不太好拓展。

三种工厂对比

简单工厂模式:适合用于创建的类少,并且创建方式比较简单的场合。

工厂模式:适合用于创建类多,并且创建方式比较复杂,写在一个类中会让类很难理解的场合。

抽象工厂:适合用于要创建的类之间存在一定的关系(例如文中将通过同一后缀的文件,生成不同类的方法定义在一个工厂内),而且这个关系不太容易变化的场景。

开源代码中的工厂模式

很多开源代码都用到了工厂模式,例如JDK中的 Calendar、DateFormat ,URLStreamHandlerFactory,Collections, Executors等类,Spring 的BeanFactory类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值