简单工厂模式 Simple Factory Pattern
基本内容
简单工厂模式指的是由一个工厂对象决定创建哪一种产品类的实例。
虽然简单工厂模式不属于GoF的23种之一,但是使用也很广泛。尤其适合工厂类创建的对象较少的场景。客户端只需要传入工厂类的参数,不需要知道创建对象过程。
优缺点
优点
解耦:
客户端与具体实现类解耦,客户端只需要知道抽象接口和工厂类,而不需要知道具体的产品类。
集中管理:
对象的创建逻辑集中在工厂类中,便于统一管理和修改。
易于扩展:
如果需要新增一种产品,只需要添加一个新的具体产品类,并在工厂类中添加相应的创建逻辑,而不需要修改客户端代码。
缺点
工厂类职责过重:
工厂类集中了所有产品的创建逻辑,一旦工厂类出现问题,可能会影响整个系统。此外如果产品类型非常多,工厂类的代码可能会变得臃肿,不易维护
实例
public interface ICourse {public void record();
}
public class CCourse implements ICourse{@Overridepublic void record() {System.out.println("正在记录C语言课程");}
}
public class JavaCourse implements ICourse{@Overridepublic void record() {System.out.println("正在录制Java课程");}
}
public class PythonCourse implements ICourse{@Overridepublic void record() {System.out.println("python课程正在录制中");}
}
public class CourseFactory {public ICourse create(String name){if("java".equals(name)){return new JavaCourse();}else if("python".equals(name)){return new PythonCourse();}else if("C".equals(name)) {return new CCourse();}else{System.out.println("没找到对应类,无法生成");return null;}}
}
public class Test {public static void main(String[] args) {CourseFactory courseFactory = new CourseFactory();ICourse python = courseFactory.create("python");python.record();}
}
在上面代码中,CourseFactory 是工厂类,负责创建 ICourse 的实现类。
客户端(Test 类)通过 CourseFactory.create(“python”) 获取 PythonCourse 实例,而不是直接 new PythonCourse()。工厂类根据 name 参数决定返回哪种课程实例。
解耦客户端与具体实现
客户端不需要知道 PythonCourse、JavaCourse 等具体类,只需要通过工厂类获取对象。
集中管理对象创建逻辑
如果新增课程(如 GoCourse),只需修改 CourseFactory,而不需要修改客户端代码。
提高代码可维护性
工厂类封装了对象创建逻辑,使代码更清晰、更易于扩展。
当然我们也可以将create方法改为静态类,方便调用。
简单工厂模式在jdk源码中也有所体现。可以看看Calendar.java里面的createCalendar方法:
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) {cal = switch (caltype) {case "buddhist" -> new BuddhistCalendar(zone, aLocale);case "japanese" -> new JapaneseImperialCalendar(zone, aLocale);case "gregory" -> new GregorianCalendar(zone, aLocale);default -> null;};}}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;}
这部分是不是和CourseFactory里面的create方法很像
优化
我们可以利用反射机制,进一步优化。
public class CourseFactory {public ICourse create(String className){try{if(!(null==className||"".equals(className))){return (ICourse) Class.forName(className).newInstance();}} catch (Exception e) {e.printStackTrace();}return null;}}
传入class的全路径名
public class Test {public static void main(String[] args) {CourseFactory courseFactory = new CourseFactory();ICourse python = courseFactory.create("com.gupaoedu.vip.design.pattern.simplefactory.PythonCourse");python.record();}
}
如果我们考虑到输入参数是字符串,可控性较差,还需要强制类型转换,可以再改一下create方法:
public ICourse create(Class<? extends ICourse> clazz){try{if(null!=clazz){return clazz.newInstance();}} catch (Exception e) {e.printStackTrace();}return null;}
public class Test {public static void main(String[] args) {CourseFactory courseFactory = new CourseFactory();ICourse python = courseFactory.create(PythonCourse.class);python.record();}
}