一、简单工厂模式定义
简单工厂提供了一个创建对象实例的功能,而无需关心其具体实现。被创建对象实例的类型可以是抽象类、接口,也可以是具体的类。
二、简单工厂模式的结构和说明
- Api: 定义所需要的功能接口。
- ApiImplA、ApiImplB: Api接口的实现类,可能会有更多个。
- Factory: 工厂类,根据条件选择合适的实现类来创建Api的对象。
三、简单工厂模式示例
假设有一个订单支付功能,我们有两种支付方式,一种支付宝支付,一种微信支付。用户在客户端进行支付的时候,需要从这两种支付方式中选取一种,来进行支付操作。
首先我们有一个支付接口:
/**
* 支付接口
*/
public interface Pay {
/**
* 支付操作方法
*/
public void pay();
}
然后我们有两个具体支付业务类:
/**
* 支付宝支付
*/
public class ZhiFuBaoPay implements Pay {
@Override
public void pay() {
System.out.println("通过支付宝进行支付...");
}
}
/**
* 微信支付
*/
public class WeiXinPay implements Pay {
@Override
public void pay() {
System.out.println("通过微信进行支付...");
}
}
定义工厂类:
/**
* 工厂类,用来创造具体支付业务对象
*/
public class PayFactory {
private PayFactory() {} // 防止客户端无谓的创建工厂实例,将构造方法私有化
/**
* 具体的创造支付业务对象实例的方法
* @param type 从外部传入的选择条件
* @return
*/
public static Pay createPay(String type){
//应该根据某些条件去选择究竟创建哪一个具体的实现对象
//这些条件可以从外部传入,也可以从其它途径获取
//如果只有一个实现,可以省略条件,因为没有选择的必要
Pay pay = null;
if("ZFB".equals(type)){
pay = new ZhiFuBaoPay();
}else if("WX".equals(type)){
pay = new WeiXinPay();
}else{
System.out.println("支付方式不正确");
}
return pay;
}
}
那么我们在客户端用的时候,就可以这样用:
public static void main(String[] args) {
Pay pay = PayFactory.createPay("ZFB");
pay.pay();// 输出:通过支付宝进行支付...
}
四、可配置的简单工厂模式示例
如果我们新增一种支付方式的话,通过上边说的模式,我们就需要修改工厂类:
/**
* 具体的创造支付业务对象实例的方法
* @param type 从外部传入的选择条件
* @return
*/
public static Pay createPay(String type){
//应该根据某些条件去选择究竟创建哪一个具体的实现对象
//这些条件可以从外部传入,也可以从其它途径获取
//如果只有一个实现,可以省略条件,因为没有选择的必要
Pay pay = null;
if("ZFB".equals(type)){
pay = new ZhiFuBaoPay();
}else if("WX".equals(type)){
pay = new WeiXinPay();
}
// 新增银联支付方式
else if("YL".equals(type)){
pay = new YinLianPay();
}
else{
System.out.println("支付方式不正确");
}
return pay;
}
这样肯定不是一个好的实现方式。一个解决的方法就是,将具体的实现类配置到配置文件中,在简单工厂的方法里面通过反射来获取实现类。
配置文件中:
ZFB=com.pay.ZhiFuBaoPay WX=com.pay.WeiXinPay YL=com.pay.YinLianPay
我们的工厂类方法可以这样实现:
/**
1. 具体的创造支付业务对象实例的方法
2. @param type 从外部传入的选择条件
3. @return
*/
public static Pay createPay(String type){
//应该根据某些条件去选择究竟创建哪一个具体的实现对象
//这些条件可以从外部传入,也可以从其它途径获取
//如果只有一个实现,可以省略条件,因为没有选择的必要
// 读取配置文件的内容,这里不介绍怎么读取配置文件了
String payClazzName = getProperty(type);
Pay pay = null;
//用反射去创建具体对象实例
try {
pay = (Pay)Class.forName(payClazzName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return pay;
}
五、简单工厂模式的优缺点
优点:
- 帮助封装。 简单工厂虽然很简单,但是非常友好的帮助我们实现了组件的封装,让组件外部真正能实现面向接口编程。
- 解耦。 通过简单工厂,实现了调用类和具体实现类的解耦。
缺点:
- 可能增加客户端的复杂度。 如果通过客户端传过来的参数来选择具体的实现类,就必须要求客户端理解各个参数代表的含义,这样会增加客户端的复杂度。
六、简单工厂模式的应用场景
- 如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装类,那么可以选择简单工厂模式。
- 如果想要把对外创建对象的职责集中管理和控制,可以选择简单工厂模式。