设计模式:JDK中的简单工厂

简介

简单工厂虽然不属于GOF23中设计模式,但却是非常简单、基础、常用的一种。
一般由一个工厂类、一个产品基类、多个产品子类构成。由工厂对象决定创建哪种产品类的实例,调用者只需要和工厂类交互。

举例

例如,我们现在有一个视频工厂,你无需直接创建具体的视频对象,工厂自会为你安排妥当。
基类,规定必要属性和抽象方法。

public abstract class Video {
    private String name;
    private String describe;

    public abstract void produce();
}

产品子类,实现抽象方法。

public class JavaVideo extends Video {
    public void produce() {
        System.out.println("This is Java class.");
    }
}
public class PythonVideo extends Video {
    public void produce() {
        System.out.println("This is python class.");
    }
}

工厂类,根据参数创建实例。

public class VideoFactory {
    public Video getInstance(String type) {
        if ("Java".equalsIgnoreCase(type)) {
            return new JavaVideo();
        } else if ("Python".equalsIgnoreCase(type)) {
            return new PythonVideo();
        }
        return null;
    }
}

客户端,只需要调用工厂方法并传参。

public class Test {
    public static void main(String[] args) {
        VideoFactory videoFactory = new VideoFactory();
        Video video = videoFactory.getInstance("Java");
        video.produce();
        video = videoFactory.getInstance("Python");
        video.produce();
    }
}

如上,工厂类的实例创建方法可以通过接收如String、int这样的标示参数创建对象,但这样不满足开闭原则,需要扩展子类时需要修改工厂类,无法对修改关闭。但如果将参数换成Class类型,直接传入你想创建的产品子类,工厂类就可以通过反射直接创建实例,可以一定程度上的满足开闭原则。

public class VideoFactory {
    public Video getVideo(Class clazz) {
        Video video = null;
        try {
            video = (Video) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return video;

    }
}

这样一来,客户端的调用方法随之变为:

public class Test {
    public static void main(String[] args) {
        VideoFactory videoFactory = new VideoFactory();
        Video video = videoFactory.getVideo(JavaVideo.class);
        video.produce();
    }
}

Java中的Calendar类,在创建Calendar实例时,就使用了简单工厂。

private static Calendar createCalendar(TimeZone zone, Locale 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 (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的UML图如下,
设计模式:JDK中的简单工厂
有三个子类BuddhistCalendar,JapaneseImperialCalendar和GregorianCalendar。所以使用者在创建Calendar时,并不需要特别创建某一具体的Calendar,工厂会根据其时区等属性,自动生产合适的Calendar对象。

上一篇:CF570D Tree Requests


下一篇:Java Calendar详解