工厂方法模式(Factory Method Pattern)是“创建对象的接口,让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类”。工厂方法模式在平常的开发工作中经常会用到。
以人类为例,先来看看一般的工厂方法模式是如何实现的:
package cn.com.fxr;
public interface Human {
void walk(); // 人类能够走路
void laugh(); // 人类能够哈哈大笑
}
package cn.com.fxr;
public class Man implements Human {
public void walk() {
System.out.println("Man's walking style...");
}
public void laugh() {
System.out.println("I'm Laughing哥...");
}
}
package cn.com.fxr;
public class Woman implements Human{
public void walk() {
System.out.println("Woman's walking style...");
}
public void laugh() {
System.out.println("Woman's laughing...");
}
}
package cn.com.fxr;
public class HumanFactory {
public static Human createHuman(
Class<? extends Human> human) {
try {
return human.newInstance();
} catch (Exception e) {
// Are you just coming from Thailand
e.printStackTrace();
}
return null;
}
}
package cn.com.fxr;
public class HumanTest {
public static void main(String[] args) {
Human man = HumanFactory.createHuman(Man.class);
Human woman = HumanFactory.createHuman(Woman.class);
man.walk();
man.laugh();
woman.walk();
woman.laugh();
}
}
这就是我们经常使用的工厂方法模式,此处在介绍一种通过枚举实现工厂方法模式的方案,其有两种方法:
(1)枚举非静态方法实现工厂方法模式
package cn.com.fxr;
public enum HumanFactoryEnum {
MAN, WOMAN;
public Human createHuman() {
switch (this) {
case MAN :
return new Man();
case WOMAN :
return new Woman();
default :
throw new AssertionError("Thailand?");
}
}
}
createHuman是一个非静态方法,只有通过MAN,WOMAN枚举项才能访问。采用这种方式实现时,客户端调用就简单了,代码如下:
public static void main(String[] args) {
Human man = HumanFactoryEnum.MAN.createHuman();
}
(2)通过抽象方法生成
枚举类型虽然不能继承,但是可以用abstract修饰其方法,此时就表示该枚举是一个抽象枚举,需要每个枚举项自行实现该方法,也就是说枚举项的类型是该枚举的一个子类。
package cn.com.fxr;
public enum HumanFactoryEnum2 {
MAN {
public Human create() {
return new Man();
}
},
WOMAN {
public Human create() {
return new Woman();
}
};
public abstract Human create();
}
从代码中可以看到,首先定义了一个抽象生产方法create,然后每个枚举项自行实现。这种方式编译后会产出两个HumanFactoryEnum2 的匿名子类,因为每个枚举项都要实现抽象create方法。
为什么要使用枚举实现工厂方法模式?
1、避免错误调用的发生
一般工厂方法模式中的生成方法(createHuman方法)可以接受三种类型的参数:类型参数(如Man.class)、String参数(判断参数是需要生成什么产品,很容易产生一大堆if-else if语句,jdk1.7也可以使用switch语句进行判断)、int参数,这三种参数都是宽泛的数据类型,很容易产生错误(如边界问题,null问题),出现这类错误编译器并不会报警,如:
public static void main(String[] args) {
Human human = HumanFactory.createHuman(Human.class);
}
Human是一个接口,完全合乎createHuman方法的要求,所以它在编译时不会报任何错误,但是一运行起来就会报InstantiationException异常。而使用枚举类型的工厂方法模式就不存在该问题了,不需要传递任何参数,只需要选择好成产什么类型的人类就行。
2、性能好,使用便捷
枚举类型的计算是以int类型的计算为基础,是基本操作,性能快,而且看客户端的调用,代码调用简单。
3、降低类间耦合
不管生成方法接受的是Class、String还是int参数,都会成为客户端类的负担。这些类并不是客户端需要的,而是工厂方法的限制必须输入的。而枚举类型的工厂方法就没有这种问题,只需要依赖工厂类就可以,完全可以无视具体类的存在。
参考资料:编写高质量代码 改善Java程序的151个建议
转载于:https://blog.51cto.com/fangxirui/1328340