创建型 - 2. 工厂模式

129 阅读6分钟

一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以工厂模式只被分成了工厂方法和抽象工厂两类。

1. 简单工厂

1.1 简单工厂的实现方式

type ConfigParser interface {
   Parse(content string) (interface{}, error)
}

type (
   JsonRuleConfigParser struct{}

   XmlRuleConfigParser struct{}

   YamlRuleConfigParser struct{}

   PropertiesRuleConfigParser struct{}
)

func (j JsonRuleConfigParser) Parse(content string) (interface{}, error) {
   //TODO implement me
   panic("implement me")
}

func (x XmlRuleConfigParser) Parse(content string) (interface{}, error) {
   //TODO implement me
   panic("implement me")
}

func (y YamlRuleConfigParser) Parse(content string) (interface{}, error) {
   //TODO implement me
   panic("implement me")
}

func (p PropertiesRuleConfigParser) Parse(content string) (interface{}, error) {
   //TODO implement me
   panic("implement me")
}

type ConfigParserFactory struct {
}

func (c ConfigParserFactory) CreateConfigParser(fileExtension string) (ConfigParser, error) {
   var parser ConfigParser
   if fileExtension == "json" {
      parser = JsonRuleConfigParser{}
   } else if fileExtension == "xml" {
      parser = XmlRuleConfigParser{}
   } else if fileExtension == "yaml" {
      parser = YamlRuleConfigParser{}
   } else if fileExtension == "properties" {
      parser = PropertiesRuleConfigParser{}
   } else {
      return nil, errors.New("unsupported file extension")
   }
   return parser, nil
}

大部分工厂类都是以“Factory”这个单词结尾的。实际上,如果 parser 可以复用,为了节省内存和对象创建的时间,我们可以将 parser 事先创建好缓存起来。


var cachedParsers map[string]ConfigParser

func init() {
   cachedParsers = make(map[string]ConfigParser)
   cachedParsers["json"] = JsonRuleConfigParser{}
   cachedParsers["xml"] = XmlRuleConfigParser{}
   cachedParsers["yaml"] = YamlRuleConfigParser{}
   cachedParsers["properties"] = PropertiesRuleConfigParser{}
}

func (c ConfigParserFactory) CreateConfigParserWithCache(fileExtension string) (ConfigParser, error) {
   parser, ok := cachedParsers[fileExtension]
   if ok {
      return parser, nil
   }
   return nil, errors.New("unsupported file extension")
}

如果 if 分支并不是很多,代码中有 if 分支也是完全可以接受的。应用多态或设计模式来替代 if 分支判断逻辑,也并不是没有任何缺点的,它虽然提高了代码的扩展性,更加符合开闭原则,但也增加了类的个数,牺牲了代码的可读性。

尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下是没有问题的。

2. 工厂方法

2.1 工厂方法的实现

如果创建单个 parser 的过程进一步复杂化,则需要单独的工厂来实现。当新增一种 parser 的时候,只需要新增一个实现了 IRuleConfigParserFactory 接口的 Factory 类即可。所以,工厂方法模式比起简单工厂模式更加符合开闭原则。

type IConfigParserFactory interface {
   CreateParser() ConfigParser
}

type (
   JsonRuleConfigParserFactory struct {
   }

   XmlRuleConfigParserFactory struct {
   }

   YamlRuleConfigParserFactory struct {
   }

   PropertiesRuleConfigParserFactory struct {
   }
)

func (j JsonRuleConfigParserFactory) CreateParser() ConfigParser {
   return JsonRuleConfigParser{}
}

func (x XmlRuleConfigParserFactory) CreateParser() ConfigParser {
   return XmlRuleConfigParser{}
}

func (y YamlRuleConfigParserFactory) CreateParser() ConfigParser {
   return YamlRuleConfigParser{}
}

func (p PropertiesRuleConfigParserFactory) CreateParser() ConfigParser {
   return PropertiesRuleConfigParser{}
}


// ConfigParserSource 是创建工厂对象的工厂类
type ConfigParserSource struct {
}

func (c *ConfigParserSource) CreateConfigParser(fileExtension string) (ConfigParser, error) {
   var factory IConfigParserFactory
   if fileExtension == "json" {
      factory = JsonRuleConfigParserFactory{}
   } else if fileExtension == "xml" {
      factory = XmlRuleConfigParserFactory{}
   } else if fileExtension == "yaml" {
      factory = YamlRuleConfigParserFactory{}
   } else if fileExtension == "properties" {
      factory = PropertiesRuleConfigParserFactory{}
   } else {
      return nil, errors.New("unsupported file extension")
   }

   parser := factory.CreateParser()
   return parser, nil
}

var cachedFactories map[string]IConfigParserFactory

func init() {
   cachedFactories = make(map[string]IConfigParserFactory)
   cachedFactories["json"] = JsonRuleConfigParserFactory{}
   cachedFactories["xml"] = XmlRuleConfigParserFactory{}
   cachedFactories["yaml"] = YamlRuleConfigParserFactory{}
   cachedFactories["properties"] = PropertiesRuleConfigParserFactory{}
}

func (c *ConfigParserSource) CreateConfigParserWithCache(fileExtension string) (ConfigParser, error) {
   factory, ok := cachedFactories[fileExtension]
   if !ok {
      return nil, errors.New("unsupported file extension")
   }
   parser := factory.CreateParser()
   return parser, nil
}

从上面的工厂方法的实现来看,一切都很完美,但是实际上存在挺大的问题。问题存在于这些工厂类的使用上。可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象

2.1 简单工厂 vs 工厂方法

工厂模式需要额外创建诸多 Factory 类,也会增加代码的复杂性,而且,每个 Factory 类只是做简单的 new 操作,功能非常单薄(只有一行代码),也没必要设计成独立的类。如果每个类功能很单薄,那就没必要设计成独立的类,这时候用简单工厂模式就好,不必要用工厂方法。

当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。而使用简单工厂模式,将所有的创建逻辑都放到一个工厂类中,会导致这个工厂类变得很复杂。

如果对象不可复用,那工厂类每次都要返回不同的对象。如果使用简单工厂模式来实现,就只能选择第一种包含 if 分支逻辑的实现方式。我们还想避免烦人的 if-else 分支逻辑,这个时候,就推荐使用工厂方法模式。

3. 抽象工厂

类只有一种分类方式。比如,在规则配置解析那个例子中,解析器类只会根据配置文件格式(Json、Xml、Yaml……)来分类。但是,如果类有两种分类方式,比如,既可以按照配置文件格式来分类,也可以按照解析的对象(Rule 规则配置还是 System 系统配置)来分类。那就会额外新增 8 个 parser,如果还是继续用工厂方法来实现的话,要针对每个 parser 都编写一个工厂类,也就是要编写 8 个工厂类。

抽象工厂就是针对这种非常特殊的场景而诞生的。可以让一个工厂负责创建多个不同类型但又相关的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。

type IConfigParserFactory interface {
   CreateRuleParser() simple_factory.RuleConfigParser // 
   CreateSystemParser() ISystemConfigParser
}

type (
   JsonRuleConfigParserFactory struct {
   }

   XmlRuleConfigParserFactory struct {
   }

   YamlRuleConfigParserFactory struct {
   }

   PropertiesRuleConfigParserFactory struct {
   }
)

func (j JsonRuleConfigParserFactory) CreateRuleParser() simple_factory.RuleConfigParser {
   return simple_factory.JsonRuleConfigParser{}
}

func (j JsonRuleConfigParserFactory) CreateSystemParser() ISystemConfigParser {
   return JsonSystemConfigParser{}
}

func (x XmlRuleConfigParserFactory) CreateRuleParser() simple_factory.RuleConfigParser {
   return simple_factory.XmlRuleConfigParser{}
}

func (x XmlRuleConfigParserFactory) CreateSystemParser() ISystemConfigParser {
   return XmlSystemConfigParser{}
}

func (y YamlRuleConfigParserFactory) CreateRuleParser() simple_factory.RuleConfigParser {
   return simple_factory.YamlRuleConfigParser{}
}

func (y YamlRuleConfigParserFactory) CreateSystemParser() ISystemConfigParser {
   return YamlSystemConfigParser{}
}

func (p PropertiesRuleConfigParserFactory) CreateRuleParser() simple_factory.RuleConfigParser {
   return simple_factory.PropertiesRuleConfigParser{}
}

func (p PropertiesRuleConfigParserFactory) CreateSystemParser() ISystemConfigParser {
   return PropertiesSystemConfigParser{}
}

// ConfigParserSource 是创建工厂对象的工厂类
type ConfigParserSource struct {
}

func (c *ConfigParserSource) CreateConfigParser(fileExtension, configType string) (interface{}, error) {
   if configType == "rule" {
      return c.createRuleConfigParser(fileExtension)
   }
   if configType == "system" {
      return c.createSystemConfigParser(fileExtension)
   }
   return nil, errors.New("unsupported config type")
}

func (c *ConfigParserSource) createRuleConfigParser(fileExtension string) (simple_factory.RuleConfigParser, error) {
   var factory IConfigParserFactory
   if fileExtension == "json" {
      factory = JsonRuleConfigParserFactory{}
   } else if fileExtension == "xml" {
      factory = XmlRuleConfigParserFactory{}
   } else if fileExtension == "yaml" {
      factory = YamlRuleConfigParserFactory{}
   } else if fileExtension == "properties" {
      factory = PropertiesRuleConfigParserFactory{}
   } else {
      return nil, errors.New("unsupported file extension")
   }

   return factory.CreateRuleParser(), nil
}

func (c *ConfigParserSource) createSystemConfigParser(fileExtension string) (ISystemConfigParser, error) {
   var factory IConfigParserFactory
   if fileExtension == "json" {
      factory = JsonRuleConfigParserFactory{}
   } else if fileExtension == "xml" {
      factory = XmlRuleConfigParserFactory{}
   } else if fileExtension == "yaml" {
      factory = YamlRuleConfigParserFactory{}
   } else if fileExtension == "properties" {
      factory = PropertiesRuleConfigParserFactory{}
   } else {
      return nil, errors.New("unsupported file extension")
   }

   return factory.CreateSystemParser(), nil
}

var cachedFactories map[string]IConfigParserFactory

func init() {
   cachedFactories = make(map[string]IConfigParserFactory)
   cachedFactories["json"] = JsonRuleConfigParserFactory{}
   cachedFactories["xml"] = XmlRuleConfigParserFactory{}
   cachedFactories["yaml"] = YamlRuleConfigParserFactory{}
   cachedFactories["properties"] = PropertiesRuleConfigParserFactory{}
}

func (c *ConfigParserSource) createRuleConfigParserWithCache(fileExtension string) (simple_factory.RuleConfigParser, error) {
   factory, ok := cachedFactories[fileExtension]
   if !ok {
      return nil, errors.New("unsupported file extension")
   }
   parser := factory.CreateRuleParser()
   return parser, nil
}

func (c *ConfigParserSource) createSystemConfigParserWithCache(fileExtension string) (ISystemConfigParser, error) {
   factory, ok := cachedFactories[fileExtension]
   if !ok {
      return nil, errors.New("unsupported file extension")
   }
   parser := factory.CreateSystemParser()
   return parser, nil
}




type ISystemConfigParser interface {
   Parse(content string) ([]byte, error)
}

type (
   JsonSystemConfigParser struct {
   }

   XmlSystemConfigParser struct {
   }

   YamlSystemConfigParser struct {
   }

   PropertiesSystemConfigParser struct {
   }
)

func (j JsonSystemConfigParser) Parse(content string) ([]byte, error) {
   //TODO implement me
   panic("implement me")
}

func (x XmlSystemConfigParser) Parse(content string) ([]byte, error) {
   //TODO implement me
   panic("implement me")
}

func (y YamlSystemConfigParser) Parse(content string) ([]byte, error) {
   //TODO implement me
   panic("implement me")
}

func (p PropertiesSystemConfigParser) Parse(content string) ([]byte, error) {
   //TODO implement me
   panic("implement me")
}