简介
简单工厂虽然不属于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图如下,
有三个子类BuddhistCalendar,JapaneseImperialCalendar和GregorianCalendar。所以使用者在创建Calendar时,并不需要特别创建某一具体的Calendar,工厂会根据其时区等属性,自动生产合适的Calendar对象。