设计模式之工厂模式解析
一、简单工厂模式
-
什么是简单工厂模式
简单工厂模式(Simple Factory Pattern) 是指由一个工厂对象决定创建出哪一种产品类的实例,它不属于GOF23种设计模式。简单工厂模式适用于工厂类负责创建对象较少的场景,且客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。来看下简单工厂模式的类图:
-
示例
创建一个标准的课程接口类
public interface ICourse { //录制视频 void record(); }
创建一个Java课程的实现类JavaCourse
public class JavaCourse implements ICourse { @Override public void record() { System.out.println("录制Java课程"); } }
再创建一个Python课程的实现类PythonCourse
public class PythonCourse implements ICourse { @Override public void record() { System.out.println("录制Python课程"); } }
创建Course课程工厂类
/** * 简单工厂模式 * 隐藏课程创建的细节,客户端只需要知晓调用参数即可,方便了客户端的使用 * @author zdp * @date 2023/9/2 17:53 */ @Slf4j public class CourseSimpleFactory { /** * 课程简单工厂 V1 * 缺点: * 如果业务继续扩展,要增加其他课程,那么工厂中的create()方法需要根据产品链的丰富来修改代码逻辑 * 不符合开闭原则,因此,可以对简单工厂继续优化 {@link CourseSimpleFactory#createV2(java.lang.Class)} * @author zdp * @date 2023/9/2 17:54 * @return ICourse */ public static ICourse createV1(String courseName) { if (JAVA.equals(courseName)) { return new JavaCourse(); } else if (PYTHON.equals(courseName)) { return new PythonCourse(); } throw new RuntimeException("un support courseName: " + courseName); } /** * 课程简单工厂 V2 * 优化后,即使业务继续扩展,工厂类也不需要再次修改了 * @author zdp * @date 2023/9/2 17:54 * @return ICourse */ public static ICourse createV2(Class<? extends ICourse> clazz) { try { Assert.notNull(clazz, "CourseFactory create ICourse clazz must not null!"); return clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { log.error("工厂类创建异常! exception: {}", e.getMessage()); throw new RuntimeException(e); } } }
简单工厂模式测试
/** * 使用简单工厂模式来创建课程, * 简单工厂模式在JDK源码中也使用到了, 如Calendar类的 Calendar.getInstance(); * 其他如: Logback中也使用到了, LoggerFactory.getLogger(); * * 简单工厂模式也存在它的缺点: * 工厂类的职责相对过重,不易于扩展过于复杂的产品结构 * @author zdp * @date 2023/9/2 17:38 */ public class SimpleFactoryTest { public static void main(String[] args) { // 简单工厂 ICourse java = CourseSimpleFactory.createV1(JAVA); java.record(); // 优化后的简单工厂 兼容性更强 ICourse javaCourse = CourseSimpleFactory.createV2(JavaCourse.class); javaCourse.record(); ICourse pythonCourse = CourseSimpleFactory.createV2(PythonCourse.class); pythonCourse.record(); } }
- 优点
- 简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,可以根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
- 客户端只需要传入一个正确的参数,就可以获取所需要的对象,无须知道其创建的细节。
- 缺点
- 工厂类的职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则。
- 不易于扩展过于复杂的产品结构。
- 优点
二、工厂方法模式
-
什么是工厂方法模式
工厂方法模式(Factory Method Pattern) 是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入的产品符合开闭原则。
工厂方法模式 主要解决产品扩展大问题,在简单工厂中,随着产品链的丰富,如果每个课程的创建逻辑有区别的话,工厂的职责会变得越来越多,不便于维护。根据单一职责我们继续拆分,Java课程由Java工厂创建,Python课程由Python工厂创建,对工厂本身也做一个抽象。来看下工厂方法模式的类图:
-
示例
ICourse 、JavaCourse、PythonCourse类同简单工程模式
再创建一个ICourseFactory工厂接口类public interface ICourseFactory { ICourse create(); }
分别创建Java课程、Python课程的工厂类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(); } }
- 优点
- 用户只需关心所需产品对应的工厂,无须关心创建细节。
- 加入新产品符合开闭原则,提高了系统的可扩展性。
- 缺点
- 工厂方法模式中类的个数容易过多,增加系统复杂度
- 增加了系统的抽象性和理解难度
- 优点
三、抽象工厂模式
-
什么是抽象工厂模式
抽象工厂模式 (Abastract Factory Pattern) 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。工厂方法模式在增加一个具体产品的时候,都要增加对应的工厂。 但是抽象工厂模式只有在新增一个产品族时才需要新增工厂。 也就是说,工厂方法模式的一个工厂只能创建一个具体产品。而抽象工厂模式的一个工厂可以创建属于一类类型的多种具体产品。工厂创建产品的个数介于简单工厂模式和工厂方法模式之间。
我的话来理解抽象工厂模式和工厂方法模式- 抽象工厂模式
- 当新增一个产品族的时候才会增加一个工厂
- 工厂方法模式
- 当新增一个具体产品的时候就会增加一个工厂
就类比生活中的例子来举例,美的、格力、海尔三大厂商会生产不同的电器
美的 洗衣机 空调 热水器 格力 洗衣机 空调 热水器 海尔 洗衣机 空调 热水器 横向就是一个产品族,纵向就是产品的等级结构,抽象工厂模式的工厂就是以产品族为维度来创建的,现在针对上面的课程优化,不仅是单独的课程内容,现在又新增了录播Video、Note功能,按照上面关系映射到本次需求内容中关系如下:
Java 课程信息course 录播videl 笔记note Python 课程信息course 录播videl 笔记note GO 课程信息course 录播videl 笔记note 现在关系理清楚了再来看下类图:
- 抽象工厂模式
-
示例
基于上面的课程再添加一个录播视频和提供笔记的功能,以上的课程不再是单独提供课程信息,还要包含录播视频和笔记才算是完整的课程。分别创建一个IVideo和INote的接口
public interface IVideo { //录播视频 void video(); }
public interface INote { //课堂笔记 void note(); }
创建一个课程的抽象工厂类
public abstract class AbstractCourseFactory { public abstract ICourse createCourse(); public abstract IVideo createVideo(); public abstract INote createNote(); }
接下来创建Java产品族,Java视频JavaVideo类、Java笔记JavaNote类
public class JavaVideo implements IVideo { public void video() { System.out.println("Java 录播"); } }
public class JavaNote implements INote { public void note() { System.out.println("Java 笔记"); } }
创建Java产品族的具体工厂JavaFactory
public class JavaFactory extends AbstractCourseFactory { @Override public ICourse createCourse() { return new JavaCourse(); } @Override public IVideo createVideo() { return new JavaVideo(); } @Override public INote createNote() { return new JavaNote(); } }
接下来创建Python产品族,Python视频PythonVideo类、Python笔记PythonNote类
public class PythonVideo implements IVideo { public void record() { System.out.println("Python 录播"); } }
public class PythonNote implements INote { public void edit() { System.out.println("Python 笔记"); } }
创建Python产品族的具体工厂PythonFactory
public class PythonFactory extends AbstractCourseFactory { @Override public ICourse createCourse() { return new PythonCourse(); } @Override public IVideo createVideo() { return new PythonVideo(); } @Override public INote createNote() { return new PythonNote(); } }
抽象工厂模式测试
public class AbstractFactoryTest { public static void main(String[] args) { // Java一系列课程 由一个工厂实现 AbstractCourseFactory javaFactory = new JavaFactory(); ICourse javaCourse = javaFactory.createCourse(); javaCourse.record(); IVideo javaVideo = javaFactory.createVideo(); javaVideo.video(); INote javaNote = javaFactory.createNote(); javaNote.note(); } }
上面的代码完整的描述了两个产品族Java课程和Python课程,也描述了两个产品等级视频和笔记。抽象工厂非常完美清晰的描述了这样一层复杂的关系。
- 优点
- 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建
- 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”
- 缺点
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
- 增加了系统的抽象性和理解难度
- 优点