设计模式-工厂模式学习之旅

947 阅读4分钟

“这是我参与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();
    }
}

四、总结

简单工厂也有它的缺点:工厂类的职责相对过重,不易于扩展过于复杂的产品结构。

工厂方法适用于以下场景:

  1. 创建对象需要大量重复的代码。
  2. 客户端(应用层)不依赖与产品类实例如何被创建,实现等细节。
  3. 一个类通过其子类来指定创建哪个对象。

工厂方法也有缺点:

  1. 类的个数容易过多,增加复杂度。
  2. 增加了系统的抽象性和理解难度。

欢迎大家关注微信公众号(MarkZoe)互相学习、互相交流。