一、简单工厂模式
简单工厂模式也被称为静态工厂模式,它是指由一个工厂对象决定创建哪一种产品的实例。 简单举个例子,在原始社会,我们需要自给自足。在工业革命之后,世界上出现了各种各样的代工厂、流水线,人们只需要买来需要的产品,并且知道如何使用就行了。那么简单工厂模式也是如此,它可以将产品的使用和生产完全分开,客户端只需要知道需要什么产品,如何来使用产品的就可以了,而具体的产品生产任务由具体的工厂类来实现。 在现实的工厂中,如果大量的、多种类型的产品都在一个工厂生产,会造成工厂负担过重、效率变低,且不利于工厂的管理。所以,简单工厂模式适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于如何创建对象不需要关心。 举例 以计算机专业的学生课程为例,计算机专业的学生要学习Java课程。我们先定义Course接口:
public interface Course {
void getCourse();
}
创建一个Java课程的实现类javaCourse类:
public class JavaCourse implements Course{
@Override
public void getCourse() {
System.out.println("Java课程");
}
}
客户端调用代码:
public static void main(String[] args) {
Course course = new JavaCourse();
course.getCourse();
}
在上面的代码中,父类Course指向子类JavaCourse的引用,应用层代码需要依赖JavaCourse。如果业务要进行扩展,学生要学习python、前端、大数据等课程,那么客户端的依赖就会变得越来越臃肿。因此,我们要想办法把这种依赖减弱,把创建细节隐藏起来。虽然在目前的代码中,创建对象的过程并不复杂,但从代码设计的角度来讲不易于扩展。现在,我们用简单工厂模式对代码进行优化。增加课程类PythonCourse:
public class PythonCourse implements Course {
@Override
public void getCourse() {
System.out.println("Python课程");
}
}
创建工厂类CourseFactory:
public class CourseFactory {
public static Course create(String name){
if("java".equals(name)){
return new JavaCourse();
} else if("python".equals(name)){
return new PythonCourse();
} else{
return null;
}
}
}
修改客户端调用代码如下:
public static void main(String[] args) {
JavaCourse course = (JavaCourse) CourseFactory.create("java");
}
可以明显的看出,客户端的调用变简单了,只需要去向工厂去索求,而不需要自己实现创建的细节。但是如果未来还开设了其它课程,我们还需要根据要求来修改代码逻辑,不符合开闭原则,因此我们可以用反射技术继续优化我们的程序,这里就不再赘述。 简单工厂模式也有缺点:工厂类的职责相对过重,不易与扩展过于复杂的产品结构,只适用于工厂类负责创建的对象较少的场景。
二、工厂方法模式
工厂方法模式是指定义一个创建对象的接口,让这个接口的实现类负责创建具体的对象,工厂方法模式是简单工厂模式的进一步抽象和推广,它让类的实例化推迟到子类中进行。工厂方法模式使用了多态性,即保持了简单工厂模式的优点,又克服了它的缺点。在工厂方法模式中,用户只需要关心所需产品对应的工厂,无需关心创建细节,而且加入新的产品时符合开闭原则。 工厂方法模式主要解决了产品的扩展问题。在简单工厂模式中,随着产品链的丰富,如果每个课程的创建逻辑有区别,则工厂的职责会变得越来越多,有点像万能工厂,不便于维护。根据单一职责原则,我们将只能继续拆分,专门的工厂干专门的事。我们令Java课程由Java工厂创建,Python课程由Python工厂创建,对工厂本身也做一个抽象。来看代码,先创建CourseFactory接口:
public interface CourseFactory {
Course create();
}
再分别创建子工厂,JavaCourseFactory类的代码如下:
public class JavaCourseFactory implements CourseFactory{
@Override
public Course create() {
return new JavaCourse();
}
}
PythonCourseFactory类的代码如下:
public class PythonCourseFactory implements CourseFactory{
@Override
public Course create() {
return new PythonCourse();
}
}
测试代码:
public static void main(String[] args) {
CourseFactory factory = new JavaCourseFactory();
Course course = factory.create();
course.getCourse();
factory = new PythonCourseFactory();
course = factory.create();
course.getCourse();
}
工厂方法模式适用于以下场景:
- 创建对象需要大量重复的代码
- 客户端(应用层)不依赖于产品类实例如何被创建、如何被实现等
- 一个类通过其子类来指定创建哪个对象
工厂方法模式也有缺点:
- 类的个数容易过多,增加复杂度
- 增加了系统的抽象性和理解难度
- 一个具体工厂只能创建一个具体的产品,虽然说解决了对工厂方法内的修改关闭,如果换一个产品的话也是需要修改具体的产品类。
三、抽象工厂模式
抽象工厂模式是指提供一个创建一系列相关或相互依赖的接口,无需指定它们的具体类。客户端(应用层)不依赖于产品类实例如何被创建、何如被实现等细节,强调的是一些列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码,需要提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。 在了解抽象工厂模式之前,我们先要了解一下产品组的概念: 产品族是指位于不同产品等级结构中,功能相关联的产品组成的家族。一般是位于不同的等级结构中的相同位置上。 仍以课程为例,现在学校有了新的标准,学生不仅可以获取到课程,还可以获取到图书资源。如果使用工厂方法模式, 如图所示,每个产品都要对应一个工厂。这本都是一个课程系列的产品,却要分成两个工厂来生产,这显然是没有必要的操作。 如果我们使用抽象工厂模式呢? 可以看出,一系列的产品只需要一个工厂去生产,更加符合我们的要求,减少了程序的负担。 Course接口如下:
public interface Course {
void getCourse();
}
Book代码如下:
public interface Book {
void getBook();
}
然后创建一个抽象工厂类CourseFactory:
public interface CourseFactory {
Course createCourse();
Book createBook();
}
接下来创建Java产品族的Java课程类javaCourse:
public class JavaCourse implements Course{
@Override
public void getCourse() {
System.out.println("Java Course");
}
}
扩展产品等级Java图书资源类JavaBook:
public class JavaBook implements Book{
@Override
public void getBook() {
System.out.println("Java Book");
}
}
创建Java产品族的具体工厂JavaCourseFactory:
public class JavaCourseFactory implements CourseFactory{
@Override
public Course createCourse() {
return new JavaCourse();
}
@Override
public Book createBook() {
return new JavaBook();
}
}
然后创建Python产品的Python课程类PythonCourse:
public class PythonCourse implements Course{
@Override
public void getCourse() {
System.out.println("Python Course");
}
}
扩展产品等级Python图书资源类PythonBook:
public class PythonBook implements Book{
@Override
public void getBook() {
System.out.println("Python Book");
}
}
创建Python产品族的具体工厂PythonCourseFactory:
public class PythonCourseFactory implements CourseFactory{
@Override
public Course createCourse() {
return new PythonCourse();
}
@Override
public Book createBook() {
return new JavaBook();
}
}
调用代码:
public static void main(String[] args) {
JavaCourseFactory factory = new JavaCourseFactory();
factory.createCourse().getCourse();
factory.createBook().getBook();
}
上面的代码完整地描述了两个产品族:Java课程和Python课程,也描述了两个产品等级:课程资源和图书资源。抽象工厂模式非常完美清晰地描述了这样一层复杂的关系。但是,不知道大家有没有发现,如果我们再继续扩展产品等级,将源码Source也加入课程,那么我们的代码从抽象工厂到具体都要全部调整,很显然不符合开闭原则,由此可知抽象工厂模式也有缺点的:
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
- 增加了系统的抽象性和理解难度。
总结
从上文来看,工厂模式好像也没有所说的那么晦涩难懂,从本文的角度来看确实如此。但是实际上,我认为工厂模式还是比较复杂的。在本文中,介绍了每种工厂模式的优缺点,那么在现实开发中,如何做到“取其精华,舍弃糟粕”呢?怎么才能做到像SpringIOC容器一样,功能强大且使用便捷呢? 在实际应用中,我们千万不能“犯强迫症”甚至“有洁癖”。在实际需求中,产品等级结构升级是非常正常的一件事情。只要不频繁升级,根据实际情况可以不遵循开闭原则。代码每半年升级一次或者每年升级一次又有何不可呢?我们要知道,原则可以帮我们较少不必要的麻烦,但必要的更新与升级缺一不可。
文章参考于《Spring5核心原理与30个类手写实战》谭勇德 著
|