1、工厂模式详情

37 阅读6分钟

1.1.1 工厂模式的由来

在现实生活中我们都知道,原始社会自给自足(没有工厂)、农耕社会有了小作坊(简单工厂,如民间酒坊)、工业革命后有了流水线(工厂方法,自产自销)、现代产业链中有现代工厂(抽象工厂,如富士康)。

我们的项目代码同样也是由简到繁一步一步迭代而来的,但对于调用者来说却越来越简单化了。

1.1.2 简单工厂模式

简单工厂模式(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 static void main(String[] args){
    ICourse course = new JavaCourse();
    course.record();
}

在上面的代码中,父类ICourse指向子类JavaCourse的引用,应用层代码需要依赖JavaCourse,如果业务扩展,继续增加PythonCourse甚至更多课程,那么客户端的依赖会变得越来越臃肿。因此,我们要想办法把这种依赖减弱,把创建细节隐藏起来。虽然在目前代码中,创建对象的过程并不复杂,但从代码设计的角度来讲不易于扩展。现在,我们用简单工厂模式对代码进行优化。

先增加课程类PythonCourse:

public class PythonCourse implements ICourse{
    @Override
    public void record(){
        System.out.println("录制Python课程");
    }
}

创建工厂类CourseFactory:

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;
       }
    }
}

修改客户端调用代码如下:

public class SimpleFactoryTest{
    public static void main(String[] args){
        CourseFactory factory = new CourseFactory();
        factory.create("java");
    }
}

当然,为了调用方便,可将工厂中的create()方法改为静态方法,下面来看一下类图,如下图所示:

简单工厂模式1.0版.png

客户端的调用变得简单了,但如果我们的业务继续扩展,要增加前端课程,那么工厂中create()方法就要每次根据产品的增加修改代码逻辑,不符合开闭原则。因此我们还可以对简单工厂模式继续优化,采用反射技术:

public class CourseFactory{
    public ICourse create(String className){
        try{
            if(!(null == className || "".equals(className))){
                rerutn (ICourse) Class.forName(className).newInstance();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

修改客户端调用代码

public static void main(String[] args){
    CourseFactory factory = new CourseFactory();
    ICourse course = factory.create("com.gupaoedu.vip.pattern.factory.simplefactory.JavaCourse");
    course.record();
}

优化之后,产品不断丰富的过程中不需要修改CourseFactory中的代码。但还有个问题是,方法参数是字符串,可控性有待提升,而且还需要强制转换,再修改一下代码:

public ICourse create(Class<? extends ICourse> clazz){
    try{
        if (null != clazz){
            return clazz.newInstance();
        }
    }catch(Exception e){
        e.printStackTrace();
    }
}

优化客户端代码

public static void main(String[] args){
    CourseFactory factory = new CourseFactory();
    ICourse course = factory.create(JavaCourse.class);
    course.record();
}

再看一下类图:

简单工厂模式2.0版.png

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

1.1.3 工厂方法模式

工厂方法模式(Factory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法模式让类的实例化推迟到子类中就进行。在工厂方法模式中用户只需关心所需产品对应的工厂,无需关心创建细节,而且加入新的产品时符合开闭原则。

工厂方法模式主要解决产品扩展的问题。在简单工厂模式中,随着产品链的丰富,如果每个课程的创建逻辑有区别,则工厂职责会变得越来越多,有点像万能工厂,不便于维护。根据单一职责原则我们将职能继续拆分,专人干专事。Java课程由Java工厂创建,Python由Python工厂创建,对工厂本身也做一个抽象。来看代码,先创建ICourseFactory接口:

public interface ICourseFactory{
    ICourse create();
}

再分别创建子工厂,JavaCourseFactory类的代码如下:

import com.gupaoedu.vip.pattern.factory.ICourse;
import com.gupaoedu.vip.pattern.factory.JavaCourse;

public class JavaCourseFactory implements ICourseFactory{
    @Override
    public ICourse create(){
        return new JavaCourse();
    }
}

PythonCourseFactory类的代码如下:

import com.gupaoedu.vip.pattern.factory.ICourse;
import com.gupaoedu.vip.pattern.factory.PythonCourse;

public class PythonCourseFactory implements ICourseFactory{
    @Override
    public ICourse create(){
        return new PythonCourse();
    }
}

测试代码如下:

public static void main(String[] args){
    ICourseFactory factory = new PythonCourseFactory();
    ICourse course = factory.create();
    course.record();
    
    factory = new JavaCourseFactory();
    course = factory.create();
    course.record();
}

现在再来看一下类图:

工厂方法模式.png

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

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

工厂方法模式也有缺点:

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

1.1.4 抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定它们的具体类。客户端(应用层)不依赖于产品类如何被创建、如何被实现等细节,强调的是一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。需要提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

讲解抽象工厂模式之前,我们要了解两个概念:产品等级结构和产品族。看下面这张图。

image.png

上图中有正方形、圆形和菱形三种图形,相同颜色深浅的代表同一产品族,相同性状的代表同一产品等级结构。同样可以做个类比,比如,美的电器生产多种家用电器,上图中颜色最深的正方形代表美的洗衣机、颜色最深的圆形代表美的空调、颜色最深的菱形代表美的热水器,最深的一排都属于美的品牌,都属于美的电器这个产品族。再看最右侧的菱形,颜色最深的代表美的热水器、第二排颜色稍浅一点的菱形可代表海信的热水器。同理,同一产品结构下还有格力热水器...