参考资料1:http://c.biancheng.net/design_pattern/
参考资料2:https://refactoringguru.cn/design-patterns/catalog
- 开发中有一个非常重要的原则“开闭原则”,对拓展开放、对修改关闭;
- 工厂模式主要负责对象创建的问题;
- 可通过反射进行工厂模式的设计,完成动态的对象创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class TestNewInstanceForFile { public static void main(String[] args) throws Exception { FileReader fr = new FileReader("files\\application.txt"); BufferedReader br = new BufferedReader(fr); String className = br.readLine(); Teacher t3 = (Teacher)createObject(className); System.out.println(t3); br.close(); }
public static Object createObject(String className) { try { Class<?> c = Class.forName(className); return c.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } }
|
1. 简单工厂模式
简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建出哪一种产品类 的实例。
优点:
- 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
- 客户端无需知道所创建具体产品的类名,只需知道参数即可。
- 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
缺点:
- 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
- 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
- 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
应用场景:
对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
1 2 3
| public interface ICourse { void record(); }
|
1 2 3 4 5
| public class JavaCourse implements ICourse { public void record() { System.out.println("录制 Java 课程"); } }
|
1 2 3 4 5 6 7
| public class SimpleFactoryTest { public static void main(String[] args) { ICourse course = new JavaCourse(); course.record(); } }
|
1.1 隐藏细节
如果业务扩展,我继续增加 PythonCourse 甚至更多,那么我们客户端的 依赖会变得越来越臃肿。因此,我们要想办法把这种依赖减弱,把创建细节隐藏
。
1 2 3 4 5
| public class PythonCourse implements ICourse { public void record() { System.out.println("录制 Python 课程"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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; } } }
|
1 2 3 4 5 6 7
| public class SimpleFactoryTest { public static void main(String[] args) { CourseFactory factory = new CourseFactory(); factory.create("java"); } }
|
1.2 使用反射
如果我们业务继续扩展,要增加前端课程,那么工厂中的 create() 就要根据产品链的丰富每次都要修改代码逻辑。不符合开闭原则。因此,我们对简单工厂还可以继续优化,可以采用反射技术
。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class CourseFactory { public ICourse create(Class<? extends ICourse> clazz) { try { if (null != clazz) { return clazz.newInstance(); } } catch (Exception e) { e.printStackTrace(); } return null; } }
|
1 2 3 4 5 6 7 8
| public class SimpleFactoryTest { public static void main(String[] args) { CourseFactory factory = new CourseFactory(); ICourse course = factory.create(JavaCourse.class); course.record(); } }
|
简单工厂模式在 JDK 源码也是无处不在,如 Calendar 类,看 Calendar.getInstance()方法:
1
| private static Calendar createCalendar(TimeZone zone, Locale aLocale){...}
|
再比如 logback,可以看到 LoggerFactory 中有多个重载的方法 getLogger():
1
| public static Logger getLogger(Class clazz) { return getLogger(clazz.getName()); }
|
2. 工厂方法模式
工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。
优点:
- 创建对象需要大量重复的代码。
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
- 一个类通过其子类来指定创建哪个对象。
缺点:
- 类的个数容易过多,增加复杂度
- 增加了系统的抽象性和理解难度
- 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
应用场景:
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌
根据单一职责原则我们将职能继续拆分,专人干专事。Java 课程由 Java 工厂创建, Python 课程由 Python 工厂创建,对工厂本身也做一个抽象。
1 2 3
| public interface ICourseFactory { ICourse create(); }
|
1 2 3 4 5
| public class JavaCourseFactory implements ICourseFactory { public ICourse create() { return new JavaCourse(); } }
|
1 2 3 4 5
| public class PythonCourseFactory implements ICourseFactory { public ICourse create() { return new PythonCourse(); } }
|
1 2 3 4 5 6 7 8 9 10 11
| 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(); } }
|
3. 抽象工厂模式
抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。
使用抽象工厂模式一般要满足以下条件:
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
优点:
抽象工厂模式除了具有工厂方法模式的优点外,其他主要如下:
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
1 2 3
| public interface IVideo { void record(); }
|
1 2 3
| public interface INote { void edit(); }
|
1 2 3 4 5 6
|
public interface CourseFactory { INote createNote(); IVideo createVideo(); }
|
1 2 3 4 5 6
| public class JavaVideo implements IVideo { public void record() { System.out.println("录制 Java 视频"); } }
|
1 2 3 4 5 6
| public class JavaNote implements INote { public void edit() { System.out.println("编写 Java 笔记"); } }
|
1 2 3 4 5 6 7 8 9
| public class JavaCourseFactory implements CourseFactory { public INote createNote() { return new JavaNote(); } public IVideo createVideo() { return new JavaVideo(); } }
|
1 2 3 4 5 6
| public class PythonVideo implements IVideo { public void record() { System.out.println("录制 Python 视频"); } }
|
1 2 3 4 5 6
| public class PythonNote implements INote { public void edit() { System.out.println("编写 Python 笔记"); } }
|
1 2 3 4 5 6 7 8 9
| public class PythonCourseFactory implements CourseFactory { public INote createNote() { return new PythonNote(); } public IVideo createVideo() { return new PythonVideo(); } }
|
1 2 3 4 5 6 7 8
| public class AbastractFactoryTest { public static void main(String[] args) { JavaCourseFactory factory = new JavaCourseFactory(); factory.createNote().edit(); factory.createVideo().record(); } }
|
上面的代码完整地描述了两个产品族 Java 课程 和 Python 课程,也描述了两个产品等级 视频 和 手记。