设计模式:工厂模式

工厂模式

提出问题

问题案例

基础概述

是什么

工厂模式的好处就是解耦

分类

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

应用

适用性

  1. 需要准备一个对象要做很多工作,而耦合在具体业务类当中并不恰当

当进行文件IO时,需要创建一个对象BufferedReader对象

1
2
3
4
5
6
7
 // 创建一个BufferedReader对象
BufferedReader bf = new BufferedReader(new FileReader(new File("aa.txt")));

// 也可能是这样的
File file = new File("aa.txt");
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
  • 当需要在很多地方使用到这个对象时,就会略显臃肿
  • 同时,如果需要更换IO读写的类,如LineNumberReader,那么工作量相对很大
  1. 有时候需要判断具体new一个什么样的类
1
2
3
4
5
6
7
8
9
10
11
public orderPizza(String type){
Pizza pizza;
//在业务类当中如此做会使得代码很脆弱而且很繁琐。
//并且如果出现了新的变化,需要反复修改。
//无法让类对修改关闭。
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}....
}

因此使用工厂模式,将对象创建委托给工厂

案例1

协作

结构

参与者

协作

  • 类关系
  • 逻辑关系

权衡

分类

结构

效果(优缺)

使用工厂模式的好处

  • 创建对象变得简单而且修改对象时能很方便
  • 面向对象的角度来看:我一个操作文件的类还要我会创建BufferReader是不是有点过分了?(职责没有分工好),交给工厂来创建对象这就很面向对象了!

专业一些即:

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

即使得解耦,低耦合

实现

实现步骤

案例1

相关模式

进阶

使用Lambda

反省总结

如何使用工厂模式

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

简单/静态工厂模式是在工厂方法模式上缩减,抽象工厂模式是在工厂方法模式上再增强

工厂方法模式

Java3y每天写代码很无聊,想要买只宠物来陪陪自己。于是乎就去宠物店看宠物啦~

作为一间宠物店,号称什么宠物都有!于是乎,店主宣传的时候就说:我的宠物店什么宠物都有

于是构建宠物的工厂就诞生了~

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

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

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

猫工厂:

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

}

狗工厂也是一样的:

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

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

动物实体类:

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

猫实体类:

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

狗实体类:

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

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

1
2
3
4
5
6
// 去找狗工厂拿一只狗过来
AnimalFactory f = new DogFactory();

// 店主就拿到了一只狗给Java3y
Animal a = f.createAnimal();
a.eat();

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

1
2
3
4
5
6
   // 去找猫工厂拿一只猫过来
AnimalFactory ff = new CatFactory();

// 店主就拿到了一只猫给Java3y
Animal aa = ff.createAnimal();
aa.eat();

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

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

优点:

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

缺点:

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

工厂方法类图:

img

简单/静态工厂模式

现在宠物店生意不好做啊,号称“什么宠物都有”,这吹过头了~~于是店主只卖两种常见的宠物了。

  • 既然就只有两种宠物的话,那就没必要有”猫厂“、”狗厂“了,一个猫狗厂就行了!

所以我们的工厂是这样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AnimalFactory {
public static Dog createDog() {
return new Dog();
}

public static Cat createCat() {
return new Cat();
}


// 外界想要猫要狗,这里创建就好了
public static Animal createAnimal(String type) {
if ("dog".equals(type)) {
return new Dog();
} else if ("cat".equals(type)) {
return new Cat();
} else {
return null;
}
}
}

三个实体还是没变(动物、猫、狗)….

那么Java3y去宠物店买猫狗的时候,告诉老板我要猫、我要狗:

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

// 拿到猫
Animal C = AnimalFactory.createAnimal("cat");
C.eat();

现在问题来了:

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

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

抽象工厂模式

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

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

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

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

具体的代码是这样的:

我们的最大工厂还是定义了创建什么动物

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

创建母猫和母狗的工厂:

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

@Override
public Animal createCat() {
return new FemaleCat();
}
}

创建公猫和公狗的工厂:

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

@Override
public Animal createCat() {
return new MaleCat();
}
}

这是所有动物都拥有的普遍行为

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

// 所有的动物都有性别
public abstract void gender();
}

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

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

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

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

猫分为公猫、母猫。狗分为公狗和母狗:

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

…..

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

  • 一类的产品我们称之为产品族
    • 猫是一类的,狗也是一类的。所以AnimalFactory定义了两类产品—>Animal createDog();Animal createCat();
  • 产品的继承结构称之为产品等级
    • 所有的动物都是会吃东西的,它们都是有性别的,这是最普遍的。所以Animal定义了两个抽象方法:public abstract void eat();public abstract void gender();
    • 所有的狗都是会吃肉的,所以Dog实现了eat()方法
      • 狗又分成了公狗和母狗,所以定义了两个类FemaleDog和MaleDog继承了Dog,实现了gender()方法
    • 所有的猫都是会吃鱼的,所以Cat实现了eat()方法
      • 猫又分成了公猫和母猫,所以定义了两个类FemaleCat和MaleCat继承了Cat,实现了gender()方法
  • 具体的工厂是面向多个产品等级结构进行生产。
    • 所以FemaleAnimalFactory定义了createDog()createCat()生产母狗和母猫
    • 所以MaleAnimalFactory定义了createDog()createCat()生产公狗和共猫
  • 找到母工厂就可以创建母猫和母狗,找到公工厂就可以创建公猫和公狗

抽象工厂模式说到底就是多了一层抽象,减少了工厂的数量

抽象工厂缺点也很明显:

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

总结

总的来说我们用简单工厂模式比较多,工厂方式模式的话代码量会比较大,抽象工厂模式的话需要业务比较大的情况下才会用到

  • 工厂模式将初始化一个类所需要的繁杂操作,包装在了一个工厂的方法当中。如考虑声明一个缓冲区,缓冲区有不同的类型,封装在不同的工厂方法当中
  • 工厂模式配合反射来使用也是极好的~

参考

  1. 工厂模式理解了没有?