[创建型模式] 工厂方法模式、简单工厂模式、抽象工厂模式(Factory Pattern)

什么是工厂模式

富士康生产电子设备,它是个工厂,电子设备是产品。

一汽集团生产汽车,它是个工厂,汽车是产品。

大连造船厂生产航母,它是个工厂,航母是产品。

我翻阅了很多书籍,也浏览了很多博客,总结一下,工厂模式大致可以细分为三种:

  1. 工厂方法模式
  2. 简单工厂模式
  3. 抽象工厂模式

这么多年,一直有个问题萦绕在我心里,从未得到过能让我信服的答案:

简单工厂模式、抽象工厂模式,这两者到底有什么区别?

最近,我又系统的复习了一遍设计模式,没有局限于设计模式的经典之作《设计模式之禅》,我仿佛开悟了一般,看到了自己多年来想要的答案。

我觉的很多书籍、很多博客,阅读量多的,点赞量高的,对这个问题的认识和解答普遍都是错误的,自己没整明白,也误导了很多后来人。

我把自己的理解写下来,与大家分享探讨,希望能够帮助那些和我有着同样问题的人,找到答案,让你们自己信服。

工厂方法模式

设计模式是解决具体问题的通用思路,它的思想是面向对象,无关于任何具体的编程语言。

工厂方法模式,就是把一个静态方法当作创建对象的工厂,用Java代码表示:

public class Product {}

public class Factory {
    // 工厂方法
    public static Product createProduct() {
        return new Product();
    }
}

工厂方法模式的特点:不需要创建工厂实例,就有工厂的能力。

上面的工厂方法只支持创建一类对象,如果要创建多类不同的对象,有两种变通方案:

  1. 工厂方法带参数,用参数区分要创建的不同类对象
  2. 每类对象分配一个工厂方法

如果是强类型的面向对象语言,选择方案1就会有个约束,不同类型的对象需要有个共同的基类,因为强类型语言的方法返回值类型是唯一的。

两种方案的代码实现:

public class Product {} // 对象基类
public class Product1 extends Product {} // 对象1
public class Product2 extends Product {} // 对象2

public class Factory {
    // 方案1:用工厂方法参数区分不同类型对象
    public static Product createProduct(int type) {
        if (type == 1) return new Product1();
        else if (type == 2) return new Product2();
        else return null;
    }
    // 方案2:一类对象一个工厂方法
    public static Product1 createProduct1() {
        return new Product1();
    }
    public static Product2 createProduct2() {
        return new Product2();
    }
}

网上很多人误以为方案2就是简单工厂模式,这种理解是不对的。

简单工厂模式

简单工厂模式是工厂方法模式的升级版。

它俩的不同点:简单工厂模式需要有工厂实例对象。

它俩的共同点:工厂返回的都是具体的产品对象。

设计模式有六大基本原则,其中有个“接口隔离原则”,在这个原则的约束下,通常还会为工厂类和产品类定义统一的接口。

下面用Java代码来展示分析三种业务场景,应用简单工厂模式,最后一种场景就是很多人以为的抽象工厂模式,但其实不是!

场景一:一个工厂,生产一种产品

// 产品类
public class Product {}

// 工厂接口
public interface IFactory{
    Product createProduct();
}

// 简单工厂模式
public class Factory implements IFactory{
    @Override
    public Product createProduct() {
        return new Product();
    }
}

这段代码看着规范,但也冗余,可以考虑用上面说的工厂方法模式简化它。

场景二:多个工厂,生产同种类型不同型号的产品

// 产品
public interface Product {}
public class Product1 implements Product {}
public class Product2 implements Product {}

// 工厂
public interface IFactory {
    Product createProduct();
}
public class Factory1 implements IFactory {
    @Override
    public Product createProduct() {
        return new Product1();
    }
}
public class Factory2 implements IFactory {
    @Override
    public Product createProduct() {
        return new Product2();
    }
}

这样的代码范式,没有异议,所有人都认可它是简单工厂模式。

场景三:多个工厂,生产不同类型不同型号的产品

这个场景下,产品就拿手机、电脑举例,两个工厂分别是华为工厂、小米工厂,分别生产:华为手机、华为电脑、小米手机、小米电脑。

// 产品接口
public interface Mobile {}
public interface Computer {}

// 手机产品
public class HuaWeiMobile implements Mobile {}
public class XiaoMiMobile implements Mobile {}

// 电脑产品
public class HuaWeiComputer implements Computer {}
public class XiaoMiComputer implements Computer {}

// 工厂接口
public interface IFactory {
    Mobile createMobile();
    Computer createComputer();
}
// 华为工厂
public class HuaWeiFactory implements IFactory {
    @Override
    public Mobile createMobile() { return new HuaWeiMobile(); }
    @Override
    public Computer createComputer() { return new HuaWeiComputer(); }
}

// 小米工厂
public class XiaoMiFactory implements IFactory {
    @Override
    public Mobile createMobile() { return new XiaoMiMobile(); }
    @Override
    public Computer createComputer() { return new XiaoMiComputer(); }
}

很多人会认为这种实现就是抽象工厂模式,理由大概都是从两个方面论证:

  1. 不同的产品族概念:华为产品族、小米产品族
  2. 不同的维度看产品:从功能角度分手机、电脑,从商家角度分华为、小米

其实,这两方面的理由表达的是同一个意思,无论哪个,都不成立。因为这都是从人的主观意识出发,站在不同视角看产品分类罢了。这样的理由根本就没有涉及到设计模式的核心要点。

试想一下:难道大名鼎鼎的简单工厂模式,就是工厂少一点,产品类型少一点这么简单的吗?这就是我多年的疑惑。

场景一到场景三的演变,工厂多了,产品多了,代码复杂了,但工厂模式的核心是没有变化的:

  1. 工厂本身有实例化的对象
  2. 工厂创建的都是具体产品

具体产品指的是MobileComputer的子类实例化对象,被工厂用一个个的new关键字生产出来。

抽象产品指的是MobileComputer本身的产品概念,它是抽象工厂模式的核心要点。

抽象工厂模式

抽象工厂模式的核心要点是:

  1. 工厂具有实例化对象
  2. 工厂生产抽象产品

怎么理解“工厂生产抽象产品”这句话呢?

还拿上面的场景三举例,意思就是:一个工厂生产出来的手机可能是华为的,也可能是小米的;同理,生产出来的电脑可能是华为的,也可能是小米的。

它和简单工厂模式的区别是:简单工厂生产的产品具有确定性,抽象工厂生产的产品具有不确定性。

这个不确定性就是抽象工厂的含义!工厂生产出来的产品,我不确定它是华为手机还是小米手机,但我知道它是个手机。

分析到这里,代码实现就是多种多样的,总体上,应该类似于这样:

public interface Product {}

// 产品接口
public interface Mobile extends Product{}
public interface Computer extends Product{}

// 手机产品
public class HuaWeiMobile implements Mobile {}
public class XiaoMiMobile implements Mobile {}

// 电脑产品
public class HuaWeiComputer implements Computer {}
public class XiaoMiComputer implements Computer {}

// 工厂接口
public interface IFactory {
    Mobile createMobile();
    Computer createComputer();
}

// 没有具体的华为工厂、小米工厂,只有一个生产手机、电脑的工厂
public class Factory implements IFactory {
    private Class<? extends Product> mobileClass;
    private Class<? extends Product> computerClass;
    
    public Factory(Class<? extends Product> mobileClass, Class<? extends Product> computerClass) {
        this.mobileClass = mobileClass;
        this.computerClass = computerClass;
    }
    @Override
    public Mobile createMobile() {
        try {
            return (Mobile) mobileClass.newInstance();
        } catch (Exception e) {
            return null;
        }
    }
    @Override
    public Computer createComputer() {
        try {
            return (Computer) computerClass.newInstance();
        } catch (Exception e) {
            return null;
        }
    }
}

其实,这个例子和这段代码还不足以完全表现出抽象工厂模式的价值。

抽象工厂的重点是对工厂进行高度抽象化,避免自身成为一个具体的工厂。

抽象化不应该涉及具体,但它应该能够表达逻辑!

所以,抽象工厂解决的问题应该是具体产品的生产逻辑,也就是说,抽象工厂负责实现一类产品的生产逻辑,但是不负责具体产品的生产。

手机、电脑,都是由很多的零部件组装出来的,可以设计一个抽象工厂,负责组装再加工的流程,具体的零部件去其它地方“采购”,把所有的零部件扔到抽象工厂里面,工厂就能给我们生产出来手机、电脑。

抽象工厂不属于华为,也不属于小米,但它能生产手机、电脑。至于手机、电脑到底是华为的,还是小米的,取决于工厂里的零部件从谁家“采购”。

只要组装再加工的生产流程不变,抽象工厂就可以垄断所有手机、电脑的生产市场,而不用关心具体是谁家的品牌。

假如现在新增需求,我要生产苹果的手机、电脑,怎么办呢?好办,抽象工厂不变,把零部件的“采购”渠道换成苹果的,就能生产出苹果的手机、电脑。

看到没,这才是抽象工厂模式真正的威力所在!

总结

总体而言,工厂模式就两个核心要点:

  1. 有没有具体的工厂实例
  2. 生产的产品是具体的,还是抽象的

有工厂实例,生产抽象产品,就是抽象工厂模式

有工厂实例,生产具体产品,就是简单工厂模式

没有工厂实例,生产具体产品,就是工厂方法模式

没有工厂实例,生产抽象产品,也是工厂方法模式

如果按这样的划分规则,工厂模式是不是就应该有四种了呢?

再来看看最开始的问题:简单工厂模式、抽象工厂模式,这两者到底有什么区别?

我想答案应该是这样的:简单工厂生产具体产品,新增一种产品,就需要新增一个工厂;抽象工厂生产抽象产品,新增一种产品,不需要新增工厂。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章