抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
举例:
为了方便和之前的工程模式作比较,还是采用简单计算器的例子,不过在实现运算功能的时候,由于数字的类型不同,比如int、float、double等,可能会产生不同的结果,所以需要扩展出多个类型的运算类。下面用抽象工厂模式实现加法、除法运算。
设计如下:
工厂类:IntFactory、FloatFactory、DoubleFactory,实现IFactory接口,都有获取加法、除法运算结果的方法,如createAdd、createDiv。
抽象产品:加法和除法,IAdd、IDiv,都有一个获取运算结果的方法。
抽象产品的实现:对于加法,有IntAdd、FloatAdd和DoubleAdd实现类。对于除法有IntDiv、FloatDiv和DoubleDiv实现类。
1.工厂类
public interface IFactory {
IAdd createAdd();
IDiv createDiv();
}
//int 工厂类
public class IntFactory implements IFactory {
@Override
public IAdd createAdd() {
return new IntAdd();
}
@Override
public IDiv createDiv() {
return new IntDiv();
}
}
//float 工厂类
public class FloatFactory implements IFactory {
@Override
public IAdd createAdd() {
return new FloatAdd();
}
@Override
public IDiv createDiv() {
return new FloatDiv();
}
}
//double 工厂类
public class DoubleFactory implements IFactory {
@Override
public IAdd createAdd() {
return new DoubleAdd();
}
@Override
public IDiv createDiv() {
return new DoubleDiv();
}
}
2.抽象产品类
public interface IAdd {
String getResult(int numberA, int numberB);
}
public interface IDiv {
String getResult(int numberA, int numberB);
}
3.抽象产品实现类
//加法
public class IntAdd implements IAdd {
@Override
public String getResult(int numberA, int numberB) {
return String.valueOf((int)numberA + numberB);
}
}
public class FloatAdd implements IAdd {
@Override
public String getResult(int numberA, int numberB) {
return String.valueOf((float)numberA + numberB);
}
}
public class DoubleAdd implements IAdd {
@Override
public String getResult(int numberA, int numberB) {
return String.valueOf((double)numberA + numberB);
}
}
//除法
public class IntDiv implements IDiv {
@Override
public String getResult(int numberA, int numberB) {
return numberB == 0 ? "除数不能为0" : String.valueOf(numberA / numberB);
}
}
public class FloatDiv implements IDiv {
@Override
public String getResult(int numberA, int numberB) {
return numberB == 0 ? "除数不能为0" : String.valueOf((float)numberA / numberB);
}
}
public class DoubleDiv implements IDiv {
@Override
public String getResult(int numberA, int numberB) {
return numberB == 0 ? "除数不能为0" : String.valueOf((double)numberA / numberB);
}
}
4.主程序
//主程序代码
public class Test1 {
public static void main(String[] args) {
IFactory factory = new IntFactory();
IAdd add = factory.createAdd();
IDiv div = factory.createDiv();
System.out.println("int工厂:7+3 = " + add.getResult(7, 3));
System.out.println("int工厂:7/3 = " + div.getResult(7, 3));
factory = new DoubleFactory();
add = factory.createAdd();
div = factory.createDiv();
System.out.println("double工厂:7+3 = " + add.getResult(7, 3));
System.out.println("double工厂:7/3 = " + div.getResult(7, 3));
factory = new FloatFactory();
add = factory.createAdd();
div = factory.createDiv();
System.out.println("float工厂:7+3 = " + add.getResult(7, 3));
System.out.println("float工厂:7/3 = " + div.getResult(7, 3));
}
}
运行结果如下:
抽象工厂模式的优点与缺点:
优点:
- 1.易于交换产品系列,由于具体工厂类在一个应用中,只需要在初始化时出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
- 2.它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
缺点:
如果需要增加新的功能,比如要增加减法类Sub,除了需要新增ISub,IntSub、FloatSub以及DoubleSub类外,还需要在工厂类接口、工厂类的每个实现类中增加获取减法类的方法createSub(),这就需要修改很多地方,增加了许多工作量。
由于工厂类通常在配置文件中定义,所以可以对抽象工厂类进行简化,采用简单工厂来改进抽象工厂。
例如,将上述的IntFactory及其实现类简化成一个类:
public class SimpleFactory {
// 根据需要,可以把工厂换成其他不同工厂
private static final String factory = "FloatFactory";
public static IAdd createAdd() {
IAdd add = null;
switch (factory) {
case "IntFactory":
add = new IntAdd();
break;
case "FloatFactory":
add = new FloatAdd();
break;
case "DoubleFactory":
add = new DoubleAdd();
break;
default:
break;
}
return add;
}
public static IDiv createDiv() {
IDiv div = null;
switch (factory) {
case "IntFactory":
div = new IntDiv();
break;
case "FloatFactory":
div = new FloatDiv();
break;
case "DoubleFactory":
div = new DoubleDiv();
break;
default:
break;
}
return div;
}
}
//主程序代码
public class Test2 {
public static void main(String[] args) {
IAdd add = SimpleFactory.createAdd();
IDiv div = SimpleFactory.createDiv();
System.out.println("float工厂:7+3 = " + add.getResult(7, 3));
System.out.println("float工厂:7/3 = " + div.getResult(7, 3));
}
}
运行结果如下:
使用反射技术+配置文件实现抽象工厂模式
和抽象工厂模式的实现方式一样,只是在工厂类的基础上,增加了一个处理类,通过配置文件去实例化相应的工厂类,不再需要由客户端去选择需要实例化的工厂类。
获取Class对象的三种方式:
- 通过类名获取---------类名.class
- 通过对象获取---------对象名.getClass()
- 通过全类名获取------Class.forName(全类名)
可以通过在配置文件中配置工厂类的实现类的全类名的方式去实例化这个类。
配置文件config.properties:
factoryName = chap15.FloatFactory
利用反射的抽象工厂处理类:
import java.io.FileInputStream;
import java.util.Properties;
public class ReflectFactory {
static Object factory;
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("D:\\workspace\\BigTalkDesignPatten\\src\\chap15\\config.properties"));
String factoryName = properties.getProperty("factoryName");
factory = Class.forName(factoryName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public static IAdd createAdd() {
return factory == null ? null : ((IFactory) factory).createAdd();
}
public static IDiv createDiv() {
return factory == null ? null : ((IFactory) factory).createDiv();
}
}
主程序:
//主程序代码
public class Test3 {
public static void main(String[] args) {
IAdd add = ReflectFactory.createAdd();
IDiv div = ReflectFactory.createDiv();
if (add != null && div != null) {
System.out.println("float工厂:7+3 = " + add.getResult(7, 3));
System.out.println("float工厂:7/3 = " + div.getResult(7, 3));
}
}
}
运行结果如下:
这样不需要客户端去主动选择,调用的都是抽象的接口,和详细的实现类进行了分离,将依赖倒转的原则发挥到了极致。