本文完整的代码在百度网盘 : 代码网盘地址,可以关注公众号“架构师路在脚下”,回复 44,获取提取码
:question: 什么时候该用工厂模式?相对于直接 new 来创建对象,用工厂模式来创建究竟有什么好处呢?
简单工厂(Simple Factory)
举例说明: 我们根据配置文件的后缀(json、xml、yaml、properties),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将存储在文件中的配置解析成内存对象 RuleConfig。
重构
1、逻辑清晰:为了让代码逻辑更加清晰,可读性更好,我们要善于将功能独立的代码块封装成函数; 2、单一职责:为了让类的职责更加单一、代码更加清晰,我们还可以进一步将 函数剥离到一个独立的类中,让这个类只负责对象的创建; 3、1+2+缓存:如果 parser 可以 复用 :cry:,为了节省内存和对象创建的时间,我们可以将 parser 事先创建好缓存起来。当调用 createParser() 方法的时候,我们从缓存中取出 parser 对象直接使用。这有点类似单例模式和简单工厂模式的结合; 大家可以动手重构一下,完整的代码在百度网盘 : 代码网盘地址,可以关注公众号“架构师路在脚下”,回复 44,获取提取码
:star::question:这里的复用指的是对象是没有状态的,即每次创建的对象都是一模一样的,不会根据不同的条件来设置对象的实例变量为不同的值。
工厂方法(Factory Method)
新增一种 parser 的时候,只需要新增一个实现了 IRuleConfigParserFactory 接口的 Factory 类即可(YamlRuleConfigParserFactory)。所以,工厂方法模式比起简单工厂模式更加符合开闭原则(对扩展开发,对修改关闭)。
但工厂类的使用上有问题,我们还是要if-else判断使用哪个工厂类。 解决方法:为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。
:question:什么时候使用工厂方法? 如果代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的函数或者类。而只有在创建逻辑比较复杂,需要组合其他类对象做各种初始化操作的时候推荐使用工厂方法模式,将复杂的逻拆分到了多个工厂类中,分解了采用简单工厂(创建逻辑在一个方法中)时代码的复杂性
抽象工厂(Abstract Factory)
在简单工厂和工厂方法中,类只有一种分类方式,如果类有两种分类方式,就会成本增加parser类。此时就可以采用抽象工厂模式,让一个工厂负责创建多个不同类型的对象。
重点回顾
:question: 什么时候简单工厂模式?
答:存在if-else来动态创建不同对象,每个对象的创建逻辑都比较简单的时候,推荐使用简单工厂模式
:question: 什么时候使用工厂模式?
答:当创建对象的逻辑比较复杂的时候使用工厂模式。
:question: 判断要不要使用工厂模式的最本质的参考标准?
1、封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。 2、代码可复用:创建代码抽离到独立的工厂类之后可以复用。 3、隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。 4、控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
问题
1、工厂模式是一种非常常用的设计模式,在很多开源项目、工具类中到处可见,比如 Java 中的 Calendar、DateFormat 类。除此之外,你还知道哪些用工厂模式实现类?可以留言说一说它们为什么要设计成工厂模式类?
2、 实际上,简单工厂模式还叫作静态工厂方法模式(Static Factory Method Pattern)。之所以叫静态工厂方法模式,是因为其中创建对象的方法是静态的。那为什么要设置成静态的呢?设置成静态的,在使用的时候,是否会影响到代码的可测试性呢?
答:会影响可测试性,因为它无法被mock(通常是mock实例的实例方法),导致其他依赖工厂类的类难以测试 静态方法不需要创建对象进行调用,创建对象是需要占用堆空间的,在方法中频繁创建对象,一般情况下方法结束后该对象就会被GC,不管是对内存使用还是GC的频率都有不好的影响。