“这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战”
一、工厂模式的历史由来
在现实生活中我们都知道,原始社会自给自足(没有工厂)、农耕社会小作坊(简单工厂,民间酒坊)、工业革命流水线(工厂方法,自产自销)。
我们的项目代码同样也是由简而繁一步一步迭代而来,但对于调用者来说确是越来越简单化。
二、 简单工厂模式
简单工厂模式( Simple Factory Pattern )是指由一个工厂对象决定创建出哪一种产品类的实例,但它不属于GOF 23种设计模式。简单工厂适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。
我们以课程为例。目前有Java、前端等课程,已经形成了一个生态。我们可以定义一个课程标准ICourse接口:
public interface ICourse {
//播放课程
void record();
}
创建Java课程的实现类JavaCourse:
public class JavaCourse implements ICourse {
@Override
public void record() {
System.out.println("播放Java课程");
}
}
客户端调用代码
public class SimpleFactoryTest {
@Test
public void Test() {
ICourse course = new JavaCourse();
course.record();
}
}
我们发现父类ICourse指向子类JavaCourse的引用,应用层代码需要依JavaCourse,如果业务扩展,继续增加AICourse课程,甚至更多,那我们客户端的依赖会变得越来越臃肿。因此,要想办法把这种依赖减弱,把创建细节隐藏。虽然目前的代码中,我们创建对象的过程并不复杂,但从代码设计角度来讲不易于扩展。现在,我们用简单工厂模式对代码进行优化。增加课程PythonCourse类:
public class PythonCourse implements ICourse {
@Override
public void record() {
System.out.println("播放Python课程");
}
}
创建课程CourseFactory工厂类:
public class CourseFactory {
public static ICourse create(String name) {
if ("java".equalsIgnoreCase(name)) {
return new JavaCourse();
} else if ("python".equalsIgnoreCase(name)) {
return new PythonCourse();
} else {
return null;
}
}
}
修改客户端调用代码:
public class SimpleFactoryTest {
@Test
public void test() {
ICourse course = CourseFactory.create("python");
course.record();
}
}
客户端调用是简单了,但如果我们业务继续扩展,需增加前端课程,那么工厂中的create()方法就要根据产品链的丰富每次都要修改代码逻辑。不符合开闭原则。因此,我们对简单工厂还可以继续优化,可以采用反射技术:
public class CourseFactory {
public static 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 {
@Test
public void test() {
ICourse course = CourseFactory.create("com.mark.factory.simple.JavaCourse");
course.record();
}
}
优化之后,产品不断丰富不需要修改CourseFactory中的代码。但是有个问题,方法参数是字符串,可控性有待提升,而且还需要强制转型。我们再修改一下代码:
public class CourseFactory {
public static ICourse create(Class<? extends ICourse> clazz) {
try {
if (clazz != null) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
优化客户端代码:
public class SimpleFactoryTest {
@Test
public void test() {
ICourse course = CourseFactory.create(PythonCourse.class);
course.record();
}
}
三、工厂方法模式
工厂方法模式(Factory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。在工厂方法模式中用户只需要关心所需产品对应的工厂,无需关心创建细节,而且加入新的产品符合开闭原则。
工厂方法模式主要解决产品扩展的问题,在简单工厂中,随着产品链的丰富,如果每个课程的创建逻辑有区别的话,工厂的职责会变得越来越多,有点像万能工厂,并不方便维护。根据单一职责原则我们将职能继续拆分,专人干专事。Java课程由Java工厂创建,Python课程由Python工厂创建,对工厂本身也做一个抽象。
我们来看代码,先创建ICourseFactory接口:
public interface ICourseFactory {
ICourse create();
}
再分别创建各自的工厂类,JavaCourseFactory类:
public class JavaCourseFactory implements ICourseFactory {
@Override
public ICourse create() {
return new JavaCourse();
}
}
PythonCourseFactory类:
public class PythonCourseFactory implements ICourseFactory {
@Override
public ICourse create() {
return new PythonCourse();
}
}
测试代码:
public class FactoryMethodTest {
@Test
public void test() {
ICourseFactory factory = new JavaCourseFactory();
ICourse course = factory.create();
course.record();
factory = new PythonCourseFactory();
course = factory.create();
course.record();
}
}
四、总结
简单工厂也有它的缺点:工厂类的职责相对过重
,不易于扩展过于复杂的产品结构。
工厂方法适用于以下场景:
- 创建对象需要大量重复的代码。
- 客户端(应用层)不依赖与产品类实例如何被创建,实现等细节。
- 一个类通过其子类来指定创建哪个对象。
工厂方法也有缺点:
- 类的个数容易过多,增加复杂度。
- 增加了系统的抽象性和理解难度。
欢迎大家关注微信公众号(MarkZoe)互相学习、互相交流。