工厂模式
概念

简单工厂
代码实现
原始写法 0.0
接下来上代码,以某学院课程为例,目前开设有 Java, Python, 大数据,人工智能等课程。
定义课程标准接口 ICourse
public interface ICourse {/*** 录制视频** @return*/void record();}
创建课程 JavaCourse 类
public class JavaCourse implements ICourse {public void record() {System.out.println("录制 Java 课程");}}
客户端调用代码
public class Test {public static void main(String[] args) {ICourse course = new JavaCourse();course.record();}}
运行结果
录制 Java 课程
上面的代码中,父类 ICourse 指向子类 JavaCourse 的引用,应用层代码需要依赖 JavaCourse, 若业务扩展,继续增加 PythonCourse 课程甚至更多,客户端的依赖会越来越臃肿。虽然现在创建对象的过程并不复杂,但从代码设计的角度来讲不易于扩展。
简单工厂模式 1.0
下面用简单工厂模式进行优化,把这种依赖减弱,把创建细节隐藏。
新增课程 PythonCourse 类
public class PythonCourse implements ICourse {public void record() {System.out.println("录制 Python 课程");}}
创建 CourseFactory 工厂类
public class CourseFactory {public ICourse create(String name) {if ("java".equals(name)) {return new JavaCourse();} else if ("python".equals(name)) {return new PythonCourse();} else {return null;}}}
修改客户端调用代码
public class SimpleFactoryTest {public static void main(String[] args) {CourseFactory courseFactory = new CourseFactory();ICourse course = courseFactory.create("python");course.record();}}
为了调用方便,CourseFactory 类的 create() 方法可以改为静态方法
public class CourseFactory {public static ICourse create(String name) {if ("java".equals(name)) {return new JavaCourse();} else if ("python".equals(name)) {return new PythonCourse();} else {return null;}}}
修改客户端调用代码
public class SimpleFactoryTest {public static void main(String[] args) {ICourse course = CourseFactory.create("python");course.record();}}
运行结果
录制 Python 课程
类图

简单工厂模式 2.0
客户端调用简单了,如果继续扩展业务,比如增加前端课程,工厂类的 create() 方法的代码逻辑每次都要修改,不符合开闭原则。
可以采用反射技术继续优化
修改 CourseFactory 工厂类
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;}}
修改客户端调用代码
public class SimpleFactoryTest {public static void main(String[] args) {ICourse course = CourseFactory.create("com.yuzhe.design.pattern.factory.JavaCourse");course.record();}}
运行结果
录制 Java 课程
简单工厂模式 3.0
优化之后,丰富产品不再需要修改 CourseFactory 中的代码,但方法参数是字符串,可控性较差,而且还需要强制类型转换。
再次优化 CourseFactory 工厂类
public class CourseFactory {public ICourse create(Class<? extends ICourse> clazz) {try {if (null != clazz) {return clazz.newInstance();}} catch (Exception e) {e.printStackTrace();}return null;}}
优化客户端代码
public class SimpleFactoryTest {public static void main(String[] args) {ICourse course = CourseFactory.create(PythonCourse.class);course.record();}}
运行结果
录制 Python 课程
类图

源码中的应用
JDK 中的 Calendar 类的 getInstance() 类,下面是具体的创建方法
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;}
slf4j 中的 LoggerFactory 有多个重载的 getLogger() 方法
public static Logger getLogger(String name) {ILoggerFactory iLoggerFactory = getILoggerFactory();return iLoggerFactory.getLogger(name);}public static Logger getLogger(Class clazz) {return getLogger(clazz.getName());}
工厂方法
代码实现
在简单工厂中,随着产品链的丰富,如果每个课程的创建逻辑有区别,工厂的职责会变得越来越重,不便维护。
根据单一职责原则,将职能拆分,专人干专事,Java 课程由 Java 工厂创建,Python 课程由 Python 工厂创建,对工厂本身也做一个抽象。
创建 ICourseFactory 接口
public interface ICourseFactory {ICourse create();}
分别创建子工厂 JavaCourseFactory 类和 PythonCourseFactory 类
public class JavaCourseFactory implements ICourseFactory {public ICourse create() {return new JavaCourse();}}
public class PythonCourseFactory implements ICourseFactory {public ICourse create() {return new PythonCourse();}}
测试代码
public class FactoryMethodTest {public static void main(String[] args) {ICourseFactory factory = new PythonCourseFactory();ICourse course = factory.create();course.record();factory = new JavaCourseFactory();course = factory.create();course.record();}}
运行结果
录制 Python 课程录制 Java 课程
类图

源码中的应用
slf4j 工厂方法模式的应用,类图如下


抽象工厂
代码实现
一个完整的课程包括笔记、视频、源码等内容,在产品等级中增加两个产品 IVideo 录播视频和 INote 课堂笔记
IVideo 接口
public interface IVideo {void record();}
INote 接口
public interface INote {void edit();}
创建抽象工厂 CourseFactory 类
public abstract class CourseFactory {public void init(){System.out.println("初始化基础数据");}protected abstract INote createNote();protected abstract IVideo createVideo();}
创建 Java 产品族,Java 视频 JavaVideo 类
public class JavaVideo implements IVideo {public void record() {System.out.println("录制 Java 视频");}}
扩展产品等级 Java 课堂笔记 JavaNote 类
public class JavaNote implements INote {public void edit() {System.out.println("编写 Java 笔记");}}
创建 Java 产品族的具体工厂 JavaCourseFactory 类
public class JavaCourseFactory extends CourseFactory {public INote createNote() {super.init();return new JavaNote();}public IVideo createVideo() {super.init();return new JavaVideo();}}
然后创建 Python 产品族,Python 视频 PythonVideo 类
public class PythonVideo implements IVideo {public void record() {System.out.println("录制 Python 视频");}}
扩展产品等级 Python 课堂笔记 PythonNote 类
public class PythonNote implements INote {public void edit() {System.out.println("编写 Python 笔记");}}
创建 Python 产品族的具体工厂 PythonCourseFactory
public class PythonCourseFactory extends CourseFactory {public INote createNote() {super.init();return new PythonNote();}public IVideo createVideo() {super.init();return new PythonVideo();}}
客户端调用
public class AbstractFactoryTest {public static void main(String[] args) {JavaCourseFactory factory = new JavaCourseFactory();factory.createNote().edit();factory.createVideo().record();}}
运行结果输出
初始化基础数据编写 Java 笔记初始化基础数据录制 Java 视频
类图

上述代码完整的描述了两个产品族:Java 课程和 Python 课程,也描述了两个产品等级:视频和笔记。抽象工厂完美清晰的描述了这样一层复杂的关系,从两个维度将一个复杂产品进行分解。
但如果继续扩展产品等级结构,如将源码 SourceCode 也加入课程中,代码从抽象工厂到具体工厂都要全部调整,显然不符合开闭原则。
