原文链接:https://zhuanlan.zhihu.com/p/3709599

工厂模式可以分成三类:

  • 简单/静态工厂模式
  • 工厂方法模式
  • 抽象工厂模式

为什么要用工厂模式?

文件IO的操作我们会经常用得到吧,BufferedReader对象经常要创建的,
你说麻烦吗?其实也不麻烦,就一行代码嘛,哪里麻烦了~如果不太熟悉IO流的同学就没有那么机灵了,创建一个BufferedReader可能就是以下的代码了:

  1. File file = new File("aa.txt");
  2. FileReader fileReader = new FileReader(file);
  3. BufferedReader bufferedReader = new BufferedReader(fileReader);

你说麻烦吗?其实也不麻烦,不就是三行代码嘛,哪里麻烦了~如果这个应用很多的类上都用到了BufferedReader对象的话,那每个类都写上这三行代码了。那你说麻烦吗?那肯定麻烦啊,还用想啊….
那有没有一种方法能够让创建对象变得简单而且修改对象时能很方便呢?
哎,工厂模式就行了。
再说从面向对象的角度来看:我一个操作文件的类还要我会创建BufferReader是不是有点过分了?(职责没有分工好)。给工厂来创建对象这就很面向对象了!

体验工厂模式

何为工厂?将我们的产品都交由工厂来生产!我现在用的iphone5s,从哪来?从富士康组装而来,富士康是工厂。我用得着知道iphone5s在富士康是怎么组装起来的吗?不需要。
来,我们来改造一下上面的例子。首先我们创建一个工厂类,它可以生产Reader对象

  1. public class ReaderFactory {
  2. public static Reader getReader() throws FileNotFoundException {
  3. File file = new File("aa.txt");
  4. FileReader fileReader = new FileReader(file);
  5. BufferedReader reader = new BufferedReader(fileReader);
  6. return reader; } }

那么我们要得到BufferReader对象就贼简单了:

  1. public class FileOperateA {
  2. public static void main(String[] args) throws FileNotFoundException {
  3. Reader reader = ReaderFactory.getReader();
  4. // 读写文件....
  5. } }

工厂将我们创建的对象过程给屏蔽了

使用工厂方法的好处

从上面的工厂模式体验我们就可以看到:

  • 我们修改了具体的实现类,对客户端(调用方)而言是完全不用修改的。
  • 如果我们使用new的方式来创建对象的话,那么我们就说:new出来的这个对象和当前客户端(调用方)耦合了,也就是,当前客户端(调用方)依赖着这个new出来的对象

工厂方法模式

很多博客都是以简单/静态工厂模式,工厂方法模式,抽象工厂模式这个顺序来讲解工厂模式的。我认为按书上的顺序比较好理解~因为简单/静态工厂模式是在工厂方法模式上缩减,抽象工厂模式是在工厂方法模式上再增强

所以我就先讲工厂方法模式了。

Java3y每天写代码很无聊,想要买只宠物来陪陪自己。于是乎就去宠物店看宠物啦~~~
作为一间宠物店,号称什么宠物都有!于是乎,店主宣传的时候就说:我的宠物店什么宠物都有
于是构建宠物的工厂就诞生了~

  1. // 号称什么宠物都有
  2. public interface AnimalFactory {
  3. // 可以获取任何的宠物
  4. Animal createAnimal();
  5. }

当然了,主流的宠物得进货一些先放在店里充充门面,一些特殊的宠物就告诉顾客要时间进货~

所以,我们就有了构建猫和狗的工厂(继承着所有宠物的工厂)

猫工厂:

  1. // 继承着宠物工厂
  2. public class CatFactory implements AnimalFactory {
  3. @Override
  4. // 创建猫
  5. public Animal createAnimal() {
  6. return new Cat();
  7. }
  8. }

狗工厂也是一样的:

  1. // 继承着宠物工厂
  2. public class DogFactory implements AnimalFactory {
  3. // 创建狗
  4. @Override
  5. public Animal createAnimal() {
  6. return new Dog();
  7. }
  8. }

嗯,还有我们的实体类:猫、狗、动物(多态:猫和狗都是动物,可以直接用动物来表示了)
动物实体类:

  1. public abstract class Animal {
  2. // 所有的动物都会吃东西
  3. public abstract void eat();
  4. }

猫实体类:

  1. public class Cat extends Animal {
  2. // 猫喜欢吃鱼
  3. @Override
  4. public void eat() {
  5. System.out.println("猫吃鱼");
  6. }
  7. }

狗实体类:

  1. public class Dog extends Animal {
  2. // 狗喜欢吃肉
  3. @Override
  4. public void eat() {
  5. System.out.println("狗吃肉");
  6. }
  7. }

那么现在Java3y想要一只狗,跟了宠物店老板说,宠物店老板就去找狗回来了:

  1. // 去找狗工厂拿一只狗过来
  2. AnimalFactory f = new DogFactory();
  3. // 店主就拿到了一只狗给Java3y
  4. Animal a = f.createAnimal();
  5. a.eat();

如果这个时候Java3y说想要一只蜥蜴怎么办啊?没问题啊,店主搞个蜥蜴工厂就好了~~

  1. // 要买蜥蜴..
  2. AnimalFactory fff = new LizardFactory();
  3. Animal aaa = ff.createAnimal();
  4. aaa.eat();

优点:

  • 1:客户端不需要在负责对象的创建,明确了各个类的职责
  • 2:如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可
  • 3:不会影响已有的代码,后期维护容易,增强系统的扩展性

缺点:

  • 1:需要额外的编写代码,增加了工作量


image.png

简单/静态工厂模式

现在宠物店生意不好做啊,号称“什么宠物都有”,这吹过头了~~于是店主只卖两种常见的宠物了。
既然就只有两种宠物的话,那就没必要有”猫厂“、”狗厂“了,一个猫狗厂就行了!
所以我们的工厂是这样子的:

  1. public class AnimalFactory {
  2. // 外界想要猫要狗,这里创建就好了
  3. public static Animal createAnimal(String type) {
  4. if ("dog".equals(type)) {
  5. return new Dog();
  6. } else if ("cat".equals(type)) {
  7. return new Cat();
  8. } else {
  9. return null;
  10. }
  11. }
  12. }

三个实体还是没变(动物、猫、狗)….
那么Java3y去宠物店买猫狗的时候,告诉老板我要猫、我要狗:

  1. // 拿到狗
  2. Animal A = AnimalFactory.createAnimal("dog");
  3. A.eat();
  4. // 拿到猫
  5. Animal C = AnimalFactory.createAnimal("cat");
  6. C.eat();

现在问题来了:

  • 1:我想要一个猪,可是我的工厂类没有猪
  • 2:我就去代码,写可以创建猪对象的
  • 3:接着,我又要其他的动物
  • 4:我还是得代码
  • 5:这就是简单工厂类的缺点:当需求改变了,我就要改代码.

简单工厂类的优点也很明显:我就一个具体的工厂来创建对象,代码量少。

抽象工厂模式

抽象工厂模式就比较复杂了,我们一般的应用都写不到。我首先来简述一下需求吧:

  • 现在非常流行在猫狗届也吹起了一股“性别风”
    • 有的喜欢公的
    • 有的喜欢母的

那我们的猫和狗都是有性别的,不是公的就是母的~~

  • 我们之前在工厂方法模式下是每个动物都开一个工厂,如果动物过多的话,那么就有很多的工厂~
  • 那现在我们可以抽取出来:每个动物不是公的就是母的~
  • 所以我们有两个工厂就足够了!

具体的代码是这样的:
我们的最大工厂还是定义了创建什么动物

  1. public interface AnimalFactory {
  2. Animal createDog();
  3. Animal createCat();
  4. }

创建母猫和母狗的工厂:

  1. public class FemaleAnimalFactory implements AnimalFactory {
  2. // 生产母狗和母猫
  3. @Override
  4. public Animal createDog() {
  5. return new FemaleDog();
  6. }
  7. @Override
  8. public Animal createCat() {
  9. return new FemaleCat();
  10. }
  11. }

创建公猫和公狗的工厂:

  1. public class MaleAnimalFactory implements AnimalFactory {
  2. // 生产公狗和公猫
  3. @Override
  4. public Animal createDog() {
  5. return new MaleDog();
  6. }
  7. @Override
  8. public Animal createCat() {
  9. return new MaleCat();
  10. }
  11. }

具体的工厂是面向多个产品等级结构进行生产。在这里是面对两大产品等级结构,一是男性族,二是女性族,根据这两大产品自行决定细分多少个想要的产品,在这里想要 公狗公猫 和 母狗母猫


具体产品的实现:
首先公猫母猫和公狗母狗的上一级是动物,所以先实现动物产品

由于想要的产品都属于动物,为了方便,所以将Animal类抽象,并让子类实现。

  1. public abstract class Animal {
  2. // 所有的动物都会吃东西
  3. public abstract void eat();
  4. // 所有的动物都有性别
  5. public abstract void gender();
  6. }

这是猫都拥有的普遍行为:

  1. public abstract class Cat extends Animal {
  2. // 猫喜欢吃鱼
  3. @Override
  4. public void eat() {
  5. System.out.println("猫吃鱼");
  6. }
  7. }

这是狗都拥有的普遍行为:

  1. public abstract class Dog extends Animal {
  2. // 狗喜欢吃肉
  3. @Override
  4. public void eat() {
  5. System.out.println("狗吃肉");
  6. }
  7. }

然后下一级分别将上一级的动物:猫、狗 细分为公猫母猫、公狗母狗进行实现。

  1. public class FemaleCat extends Cat {
  2. public void gender() {
  3. System.out.println("I am a female Cat");
  4. }
  5. }

……….

简单来说:工厂方法模式的工厂是创建出一种产品,而抽象工厂是创建出一类产品。

  • 一类的产品我们称之为产品族
  • 产品的继承结构称之为产品等级
  • 具体的工厂是面向多个产品等级结构进行生产
    • 所以FemaleAnimalFactory定义了createDog()和createCat()生产母狗和母猫
    • 所以MaleAnimalFactory定义了createDog()和createCat()生产公狗和共猫
  • 找到母工厂就可以创建母猫和母狗,找到公工厂就可以创建公猫和公狗

image.png

抽象工厂模式说到底就是多了一层抽象,减少了工厂的数量
抽象工厂缺点也很明显:

  • 难以扩展产品族—->如果我再要宠物猪的话
    • 那我要修改AnimalFactory、FemaleAnimalFactory、MaleAnimalFactory这些类了~


总结

总的来说我们用简单工厂模式比较多,工厂方式模式的话代码量会比较大,抽象工厂模式的话需要业务比较大的情况下才会用到(如果有更好的理解方式不妨在评论区留言,一起交流交流涨涨见识~~)

  • 工厂模式配合反射来使用也是极好的~