定义与类型
定义:由一个工厂对象决定创建出哪一种“产品”类型
类型:创建型,但不属于GOF23种设计模式
适用场景
- 工厂类负责创建的对象比较少
- 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象逻辑并不关心
优点
只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节
缺点
- 工厂类的职责相对过重,增加新的产品,需要修改工厂类的判断逻辑,违背开闭原则
- 无法形成基于继承的等级结构
Coding
对于GOF23中设计模式中,带工厂的只有抽象工厂和工厂方法两种模式
package com.jpg.creational.simplefactory;
public abstract class Video {
/**
* 录制视频的抽象方法
*/
public abstract void produce();
}
package com.jpg.creational.simplefactory;
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
package com.jpg.creational.simplefactory;
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}
package com.jpg.creational.simplefactory;
public class Test {
public static void main(String[] args) {
/*
* 当前写法的缺点就是我们Test类,也就是我们的客户端类
* 是非常的依赖JavaVideo这个类的,当我们需要录制Python视频的时候
* 就需要将JavaVideo改为PythonVideo,同时还需要重新导入相对应的包
*/
// Video video = new JavaVideo();
// video.produce();
/*
* 如何使我们的Test类不依赖对应的类?
* 将整个的生产过程,放到某个类中,使我们的应用层的类不依赖具体的类
*/
// VideoFactory videoFactory = new VideoFactory();
// Video video = videoFactory.getVideo("java");
// if (video == null) {
// return;
// }
// video.produce();
//通过反射,满足开闭原则
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo(JavaVideo.class);
if (video == null) {
return;
}
video.produce();
}
}
package com.jpg.creational.simplefactory;
public class VideoFactory {
/*
* 如果此时在添加一个前端的课程,需要在此类中
* 再写一个else if,需要对这个类进行修改
* 不符合开闭原则,对修改关闭,对扩展开放
* 我们的理想状态是,写一个前端课程的类,继承Video
* 就可以在应用层,直接使用,不用修改代码了
*
* 这里也可以写成静态方法,这样我们就不用创建工厂对象了
* 与此同时,这个方法就不能被继承,当没有被继承需要的时候
* 可以写成静态方法
*/
public Video getVideo(String type) {
if ("Java".equalsIgnoreCase(type)) {
return new JavaVideo();
} else if ("Python".equalsIgnoreCase(type)) {
return new PythonVideo();
}
return null;
}
//通过反射,来弥补简单工厂的扩展性,也就是从一定程度上满足
//开闭原则,当需要添加一个前端课程的时候,只需要传入这个课程的.class即可
//工厂类不需要改变
public Video getVideo(Class c) {
Video video = null;
try {
video = (Video) Class.forName(c.getName()).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
return video;
}
}
在没有引入工厂的情况下Test直接依赖具体的类
引入工厂类之后,Test直接依赖工厂
JDK源码解析
在java.util.Calendar,这个类中,在获取Calender对象的时候,使用到了简单工厂模式
/**
* Gets a calendar using the default time zone and locale. The
* <code>Calendar</code> returned is based on the current time
* in the default time zone with the default
* {@link Locale.Category#FORMAT FORMAT} locale.
*
* @return a Calendar.
*/
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
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 no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
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;
}