工厂方法模式(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、降低类间耦合

不管生成方法接受的是ClassString还是int参数,都会成为客户端类的负担。这些类并不是客户端需要的,而是工厂方法的限制必须输入的。而枚举类型的工厂方法就没有这种问题,只需要依赖工厂类就可以,完全可以无视具体类的存在。


参考资料:编写高质量代码 改善Java程序的151个建议