工厂模式在Spring框架和JDK源码中的应用
这一节我们看看工厂模式在Spring框架和JDK源码中的应用:
一、Spring中的工厂模式
1.1 简单工厂BeanFactory
Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象
我们可以看到DefaultListableBeanFactory实现了这个工厂方法:
我们看一下接口BeanFactory:
DefaultListableBeanFactory和BeanFactory的关系:
1.2 工厂方法FactoryBean
实现原理:
实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是Spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getOjbect()方法的返回值。
AbstractFactoryBean:
我们看下FactoryBean的一个小栗子可能会比较有感觉:
使用XML的注入方式:
具体的测试代码:
二、JDK中的工厂模式
在JDK源码中 ,java.util.Calendar使用了工厂模式的简单工厂模式。
找到getInstance()方法,跟进去可以看到如下的代码:
工厂模式实战之不同的支付渠道
在实际项目中,我们会引入支付宝支付和微信支付,如果是按照常规的设计我们会这么设计:
设计两个支付类AliPay和WeixinPay,然后在调用的时候,实例化相应的类来进行发起支付,这种方式对于调用者而言,不是很友好,扩展性不强。那么我们就可以使用简单工厂模式或者工厂方法模式进行优化。
这里我们使用简单工厂模式优化一下,工厂方法模式思路和我们上面讲的BMW是一样的。
一、工厂模式实战之不同的支付渠道
1.1 类图
只要了解了简单工厂模式,具体的编码还是很简单的,我们来看下类图:
1.2 编码
有了类图,编码就很简单了。
1.2.1 抽象产品角色
在接口PayChannel定义了pay()的方法:
package com.kfit.factory.example.v2;
import java.math.BigDecimal;
/\*\*
\* 支付渠道接口
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-20
\* @slogan 大道至简 悟在天成
\*/
public interface PayChannel {
/\*\*
\* 定义支付方法
\* @param price 支付的价格
\* @return 调用是否成功,true:成功,false:失败.
\*/
boolean pay(BigDecimal price);
}
复制代码
1.2.2****具体产品角色
这里主要有AliPay和WeixinPay:
AliPay:
package com.kfit.factory.example.v2;
import java.math.BigDecimal;
/\*\*
\*
\* 支付宝支付调用
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-20
\* @slogan 大道至简 悟在天成
\*/
public class AliPay implements PayChannel{
@Override
public boolean pay(BigDecimal price){
System.out.println("调用支付宝SDK发起支付,价格:"+price);
return true;
}
}
复制代码
WeixinPay:
package com.kfit.factory.example.v2;
import java.math.BigDecimal;
/\*\*
\*
\* 微信支付调用
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-20
\* @slogan 大道至简 悟在天成
\*/
public class WeixinPay implements PayChannel{
@Override
public boolean pay(BigDecimal price){
System.out.println("调用微信支付SDK发起支付,价格:"+price);
return true;
}
}
复制代码
1.2.3****具体工厂角色
具体工厂角色PayChannelFactory:
package com.kfit.factory.example.v2;
/\*\*
\* 支付方式工厂类
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-20
\* @slogan 大道至简 悟在天成
\*/
public class PayChannelFactory {
/\*\*
\* 这个type一般是前端会使用单选按钮进行让用户选择,我们就可以在单选按钮上进行设置对应的值为0或者1
\* @param type 0:支付宝,1:微信支付.
\* @return
\*/
public static PayChannel getPayChannel(int type){
PayChannel payChannel = null;
if(type == 0){
payChannel = new AliPay();
//在这里设置发起支付需要的一些配置信息,比如appId,加密方式等
}else if(type == 1){
payChannel = new WeixinPay();
//在这里设置发起支付需要的一些配置信息,比如appId,加密方式等
}else{
throw new RuntimeException("不支持支付方式,type取值\[0|1\],0:支付宝,1:微信支付");
}
return payChannel;
}
}
复制代码
1.3 测试
package com.kfit.factory.example.v2;
import java.math.BigDecimal;
/\*\*
\* 测试支付
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-20
\* @slogan 大道至简 悟在天成
\*/
public class Test {
/\*\*
\* 这种方式对于使用者而言,不是很优化,所以我们进行优化。
\*
\* @param args
\*/
public static void main(String\[\] args) {
//使用支付宝支付
PayChannel payChannel = PayChannelFactory.getPayChannel(0);
payChannel.pay(new BigDecimal(88.88));
//使用微信支付
payChannel = PayChannelFactory.getPayChannel(1);
payChannel.pay(new BigDecimal(88.88));
}
}
复制代码
工厂模式总结
这一节我们轻松一下,我们对于前面的简单工厂、工厂方法、抽象工厂做个总结。
一、工厂模式
1.1 定义
简单工厂(Simple Factory):由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(继承自一个父类或接口)的实例。
工厂方法(Factory Method):定义工厂父类指定公共的接口,而子类工厂则负责生成具体的对象(多个工厂)。
抽象工厂(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例(工厂中可以生产多个产品)。
1.2 主要对象
简单工厂:具体工厂(一个)、抽象产品、具体产品
工厂方法:抽象工厂、具体工厂(多个)、抽象产品、具体产品
抽象工厂:抽象工厂、具体工厂(多个)、抽象产品(族)、具体产品
抽象工厂的多个有两层含义:多个工厂的实现,每个实现有多个创建产品的方法。
简单工厂模式主要对象:
简单工厂模式又叫做静态工厂方法模式,不属于23种GOF设计模式之一。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
工厂(Factory):负责实现创建所有实例的内部逻辑,并提供一个外界调用的方法,创建所需的产品对象。
抽象产品(Product):负责描述产品的公共接口
具体产品(ConcreteProduct):描述生产的具体产品。
工厂方法模式主要对象:
抽象工厂(Abstract Factory):描述具体工厂的公共接口
具体工厂(ConcreteFactory):描述具体工厂,创建产品的实例,供外界调用
抽象产品(Product):负责描述产品的公共接口
具体产品(ConcreteProduct):描述生产的具体产品
抽象工厂模式主要对象:
抽象工厂(AbstractFactory):描述具体工厂的公共接口,一般可以生成两个产品
具体工厂(ConcreteFactory):描述具体工厂,创建产品的实例,供外界调用
抽象产品(族)(AbstractProduct):描述抽象产品的公共接口
具体产品(ConcreteProduct):描述具体产品的公共接口
1.3 区别
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
1.4 适用场景
简单工厂模式:
工厂类中创建的对象不能太多,否则工厂类的业务逻辑就太复杂了,其次由于工厂类封装了对象的创建过程,所以客户端应该不关心对象的创建。适用场景:
(1)需要创建的对象较少。
(2)客户端不关心对象的创建过程。
工厂方法模式:
和简单工厂对比一下,最根本的区别在于,简单工厂只有一个统一的工厂类,而工厂方法是针对每个要创建的对象都会提供一个工厂类。适用场景:
(1)客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
(2)创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
(3)客户不关心创建产品的细节,只关心产品的品牌
抽象工厂模式:
抽象工厂的适用场景:
(1)和工厂方法一样客户端不需要知道它所创建的对象的类。
(2)需要一组对象共同完成某种功能时。并且可能存在多组对象完成不同功能的情况。
(3)系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)。
二、小结
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。
在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。
经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。