前言
不管工作几年的开发小伙伴,相信工厂模式对都不陌生,甚至在日常的开发中也不经意间会使用,通常对象创建都是通过new 的方式,后面接触到spring 框架,可以在编码过程实例化对象中减少了 new 的使用,spring帮我们在容器创建好了对象,我们在类中依赖注入即可使用,这便是IOC(控制反转)的原理,spring中就是使用工厂模式来实例化对象。
工厂模式一般被分为两种,第一种是简单工厂模式,第二种是工厂方法模式,结合日常开发中的实际业主场景,如在某一个业务平台中支付业务领域中有账户现金支付(这里指用户在平台中给自己账户充值金额),会员卡支付,微信支付,银联支付等等,在系统中将以上支付汇集到一起,下面代码中以支付工厂的方式实现用户支付。
一、简单工厂模式(Simple Factory)
一般在工厂类中提供一个封装的静态工厂方法(又称静态工厂),用于隐藏对象初始化细节,让代码可以专注于使用该工厂化后初始化后的对象,而不用关心类的初始化过程。
以实际业务应用中的支付场景为例子 将四种支付对象统一抽象成 AbstractPayment,抽象的方式使用 abstract (抽象类),在实际业务中使用 interface 来抽象也可以,毕竟工厂模式属于创建型模式,主要还是用来创建对象,不是对其行为的约束
//支付对象
public abstract class AbstractPayment {
//支付对象中有支付方法
public abstract APIResult pay(BigDecimal amount, String userId, String payCode);
}
//1 账户现金支付对象
public class CashPayment extends AbstractPayment {
//账户现金支付逻辑
public APIResult pay(BigDecimal amount, String userId, String payCode){
//1.扣减账户余额
System.out.println("扣减账户余额");
//2.消费账单记录
System.out.println("消费账单记录");
return APIResult.success();
}
}
//2.银联账户支付对象
public class UnionPayment extends AbstractPayment {
//银联账户支付逻辑
public APIResult pay(BigDecimal amount, String userId, String payCode){
//1.商户配置信息获取
System.out.println("商户配置信息获取");
//2.调用银联支付接口
System.out.println("调用银联支付接口");
//3.记录消费账单
System.out.println("记录消费账单");
return APIResult.success();
}
}
//3:会员卡支付对象
public class VipCardPayment extends AbstractPayment {
//会员卡支付逻辑
public APIResult pay(BigDecimal amount, String userId, String payCode){
//1.扣钱会员卡余额
System.out.println("扣钱会员卡余额");
//2.记录消费账单记录
System.out.println("记录消费账单记录");
//3.会员积分计算
System.out.println("会员积分计算");
return APIResult.success();
}
}
//4: 微信支付对象
public class WechatPayment extends AbstractPayment {
//1.微信支付逻辑
public APIResult pay(BigDecimal amount, String userId, String payCode){
//1.获取对接配置信息
System.out.println("获取对接配置信息");
//2.调用微信接口扣款
System.out.println("调用微信接口扣款");
//3.记录消费账单信息记录
System.out.println("记录消费账单信息记录");
return APIResult.success();
}
}
简单工厂创建
根据不同的类型来在工厂类PaymentFactory 中实例化对象
public class PaymentFactory {
public static IPaymentService createPayment(Integer payType){
switch (payType){
case 1:
return new CashPayment();
case 2:
return new UnionPayment();
case 3:
return new VipCardPayment();
case 4:
return new WechatPayment();
default:
return null;
}
}
测试方法
public class PayFactoryTest {
@Test
//简单工厂测试
public void payTest(){
//通过简单工厂创建对象
IPaymentService payment = PaymentFactory.createPayment(1);
//执行扣费方法
payment.pay(new BigDecimal("100"),"zyh","435436546");
}
}
二、工厂方法模式(Factory Method)
为创建一个对象定义一个接口,但是让子类决定实例化哪个类,就是让实例化的过程延迟到子类。
在上面代码的基础上,使用工厂方法模式思想去实现
//定义创建支付对象接口
public interface IPaymentFactory {
public AbstractPayment createPayment();
}
//1.账户现金支付对象
public class CashPaymentService implements IPaymentFactory {
@Override
public AbstractPayment createPayment() {
return new CashPayment();
}
}
//银联支付对象
public class UnionPaymentService implements IPaymentFactory {
@Override
public AbstractPayment createPayment() {
return new UnionPayment();
}
}
//会员卡支付对象
public class VipCardPaymentService implements IPaymentFactory {
@Override
public AbstractPayment createPayment() {
return new VipCardPayment();
}
}
//微信支付对象
public class WechatPaymentService implements IPaymentFactory {
@Override
public AbstractPayment createPayment() {
return new WechatPayment();
}
}
//工厂方法测试
@Test
public void payFactoryMethod(){
//通过工厂方法模式创建(账户现金)对象
IPaymentFactory cashPaymentService = new CashPaymentService();
AbstractPayment payment = cashPaymentService.createPayment();
//执行扣费方法
payment.pay(new BigDecimal("100"),"zyh","435436546");
}
三、实际开发中的工厂模式
1.上面只是介绍的工厂模式的思想,随着现在框架的使用,实际开发中获取实例化对象方式都不需要自己去显式创建(new 的方式),上述的业务场景中可能更多的写法如下下面的代码没有显示的使用工厂模式而是使用策略模式,辅以简单的工厂方法来管理策略实例,传统的工厂方法模式通常是通过继承和子类化来创建对象,但是在 获取某一支付类型的对象中使用 PaymentFactoryRef 类充当了简单的工厂角色
下面的代码获取相应的支付对象也是使用了工厂模式,只不过工厂中创建对象的方式是通过Spring框架中的BeanFactory 接口中 getBean()方法来实现的,如果需要看具体策略模式的代码 可以看文章
链接: 策略模式
//定义支付接口
public interface IPaymentService {
/**
* 支付方法
* @param amount
* @param userId
* @param payCode
* @return
*/
APIResult pay(BigDecimal amount, String userId, String payCode);
}
//通过Spring 依赖注入对象的方式出始化对象
@Service
public class CashPaymentServiceimpl implements IPaymentService {
@Override
public APIResult pay(BigDecimal amount, String userId, String payCode) {
return null;
}
}
@Service
public class UnionPaymentServiceimpl implements IPaymentService {
@Override
public APIResult pay(BigDecimal amount, String userId, String payCode) {
return null;
}
}
@Service
public class VipCardPaymentServiceimpl implements IPaymentService {
@Override
public APIResult pay(BigDecimal amount, String userId, String payCode) {
return null;
}
}
@Service
public class WechatPaymentServiceimpl implements IPaymentService {
@Override
public APIResult pay(BigDecimal amount, String userId, String payCode) {
return null;
}
}
//获取支付对象的入口
@Component
public class PaymentFactoryRef {
//支付对象的集合
public static final Map<Integer,IPaymentService> paymentServiceHashMap = new ConcurrentHashMap<>();
//依赖注入
@Resource(name = "cashPaymentServiceimpl")
private IPaymentService cashPaymentServiceimpl;
@Resource(name = "unionPaymentServiceimpl")
private IPaymentService unionPaymentServiceimpl;
@Resource(name = "vipCardPaymentServiceimpl")
private IPaymentService vipCardPaymentServiceimpl;
@Resource(name = "wechatPaymentServiceimpl")
private IPaymentService wechatPaymentServiceimpl;
//项目初始化时候实例化对象到map中
@PostConstruct
public void genPaymentFactoryInit(){
paymentServiceHashMap.put(1,cashPaymentServiceimpl);
paymentServiceHashMap.put(2,unionPaymentServiceimpl);
paymentServiceHashMap.put(3,vipCardPaymentServiceimpl);
paymentServiceHashMap.put(4,wechatPaymentServiceimpl);
}
}
//上面初始化paymentServiceHashMap
//的过程如果也可以通过注解的方式动态获取
//IPaymentService 的实例:AnnotationUtils.findAnnotation()
//实际业务场景的使用(方法测试)
@Test
public void getPayFactoryAct(){
//获取支付对象
IPaymentService iPaymentService = PaymentFactoryRef.paymentServiceHashMap.get(1);
iPaymentService.pay(new BigDecimal("100"),"zyh","56756757");
}
2.补充一个jdk 中的工厂模式实例(Calendar 类)
根据指定时区和地区获取日历对象 : public static Calendar getInstance(TimeZone zone,Locale aLocale){}; 源码如下
用户无需关注 Calendar 的创建过程,只需要传指定的参数来就能获取到不同的 Calendar 实例,或者不传参数,获取默认的 Calendar 实例
// 日历在代码中抽象成 Calendar
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
//简单工厂模式,创建 Calendar 实例
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
//相关逻辑省略.......
//代码中根据 aLocale 传参类型来实例化对象
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
}
Calendar 的符合工厂模式的特性有:
抽象类: Calendar 是抽象类,不直接实例化。
隐藏实现: 用户通过 getInstance() 获取对象,无需关心具体子类(如 GregorianCalendar、BuddhistCalendar)。
多态性: 返回的实例可能是不同子类,但统一表现为 Calendar 类型。
总结
工厂模式的核心:
1.不直接使用 new 创建对象,而是通过 工厂类 来实例化对象。
2.隐藏对象创建的细节,客户端代码只需调用工厂方法,无需关心具体实现类。
简单工厂和工厂方法模式的区别:
工厂模式 | 特点 | 适用场景 |
---|---|---|
简单工厂(Simple Factory) | 一个工厂类负责创建所有对象,通过参数决定具体实例 | 对象创建逻辑简单,变化较少 |
工厂方法(Factory Method) | 定义一个创建对象的接口,让子类决定实例化哪个类 | 需要扩展性强,支持多种产品 |
工厂模式的好处
1.降低代码之间的耦合
实例化对象时不依赖具体类,只依赖接口或抽象类。
修改具体实现类时,只需调整下工厂类简单工厂模式补充子类时需要调整工厂类
。
2. 封装创建逻辑
将对象的创建逻辑集中管理,避免重复代码。
适用于复杂对象初始化(如数据库连接、线程池等)。
3. 支持多态
工厂可以返回不同的子类对象,客户端代码统一使用接口操作。
4. 易于扩展
以工厂方法模式为例:新增时只需扩展工厂中的子类,由子类来创建具体对象,无需修改工厂类中的逻辑(符合开闭原则)
这一点是优势也有弊端:优势是便于扩展子类,弊端是子类可能会无限膨胀