设计模式(四):抽象工厂模式

Abstract Factory(抽象工厂)

在此之前,已经学习过了Factory Method(工厂方法),抽象工厂和工厂方法这两个模式都属于创建型设计模式,那么根据[工厂]这个词,大概也能猜出这两个模式都是用来“生产”的

那么这两种用来“生产”的模式 ——— 抽象工厂和工厂方法,这两个设计模式有什么区别呢?或者说也两个模式“生产”的东西有什么不同的吗?

在之前工厂方法的简单实现中,举了个工厂制作蛋糕的例子,那么现在我们的蛋糕店需要生产两种蛋糕,分别为草莓口味的红色的爱心型蛋糕和芒果口味的黄色的方形蛋糕,这两个都是具体的产品,换句话说,对于草莓味心型蛋糕,心型模具和草莓味奶油都是只用来生产这类蛋糕的,那如果我们想做一个爱心型的芒果蛋糕呢?难道还有做一个只属于芒果蛋糕的模具吗?

不,当然不是,在日常生活中,对于这种情况,应该是拿一个爱心模具来做一类爱心型蛋糕,而不会说一种蛋糕做一个模具

到这里,或许对于抽象工厂和工厂方法的区别还是有点模糊

那么,现在来总结一下,

  • 如果是用来生成一个复杂的对象,他的内部元素无法抽象出来时,选择工厂方法模式
  • 如果是用来生产具体细节抽象的产品,产品的内部元素可变,选择抽象工厂

说了这么多,但是还没有见到抽象模式的真身,下面先介绍一下抽象工厂模式,然后,再重新打造我们的工厂,让其能够生产更多类型的蛋糕

Abstract Factory(抽象工厂)的UML类图

  • AbstractProductA:抽象产品A
  • ProductA1:具体产品A1
  • ProductA2:具体产品A2
  • AbstractProductB:抽象产品B
  • ProductB1:具体产品B1
  • ProductB2:具体产品B2
  • AbstractFactory:抽象工厂,内部有两个方法,分别用来生产不同的产品,最后成一个整体
  • ConcreteFactory1:具体工厂1
  • ConcreteFactory2:具体工厂2

先创建产品的抽象类
AbstractProductA

1
2
3
public abstract class AbstractProductA {
public abstract void doA();
}

AbstractProductB

1
2
3
public abstract class AbstractProductB {
public abstract void doB();
}

然后再来打造具体的产品细节
ConcreteProductA1

1
2
3
4
5
6
7
public class ConcreteProductA1 extends AbstractProductA {
@Override
public void doA() {
System.out.println("Concrete Product A 1");
}

}

ConcreteProductA2

1
2
3
4
5
6
7
public class ConcreteProductA2 extends AbstractProductA {
@Override
public void doA() {
System.out.println("Concrete Product A 2");
}

}

ConcreteProductB1

1
2
3
4
5
6
7
public class ConcreteProductB1 extends AbstractProductB {
@Override
public void doB() {
System.out.println("Concrete Product B 1");
}

}

ConcreteProductB2

1
2
3
4
5
6
7
public class ConcreteProductB2 extends AbstractProductB {
@Override
public void doB() {
System.out.println("Concrete Product B 2");
}

}

OK,关于具体的产品细节已经做好了,现在最关键的就是通过工厂,来将这些细节组装成一个具体的产品

首先,在打造一个抽象工厂

AbstractFactory

1
2
3
4
5
6
public abstract class AbstractFactory {
// 生产产品细节A
public abstract AbstractProductA createProductA();
// 生产产品细节B
public abstract AbstractProductB createProductB();
}

好了,接下来就是根据需要打造具体的工厂了
ConcreteFactory1:生产产品细节A1和产品细节B2

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteFactory1 extends AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}

@Override
public AbstractProductB createProductB() {
return new ConcreteProductB2();
}
}

ConcreteFactory2:生产产品细节A2和产品细节B1

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteFactory2 extends AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA2();
}

@Override
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}

好了,现在我们只需要通过这两个工厂,就能得到一个具有A和B两种不同细节的产品

抽象工厂模式的简单实现

还是挺简单的,那么,接下来,借助上面的经验,是时候将蛋糕店好好装修一番了,让蛋糕店可以生产各种各样的产品

首先,分析一下,对于蛋糕

  • 按照造型分,我们有爱心型的和方形的,
  • 按照奶油分,我们有红色的草莓味和黄色的芒果味

那么根据调查发现(是我瞎说的~),现在爱心型的草莓味蛋糕和芒果味的方形蛋糕是卖的最火的,所以,蛋糕店也需要与时俱进,将要生产这种两种蛋糕

好了,分析完了,我们先根据造型,打造一个抽象造型产品(或者说是蛋糕模板也可以啦~)

抽象造型产品

1
2
3
4
public abstract class CakeStyle {
public abstract void style();
}

再根据奶油,打造一个抽象奶油产品

抽象奶油产品

1
2
3
public abstract class CakeCream {
public abstract void cream();
}

OK,下面分别开始制作爱心型模板,方形模板,草莓味奶油,芒果味奶油

爱心型模板

1
2
3
4
5
6
public class HeartStyle extends CakeStyle {
@Override
public void style() {
System.out.println("Heart Style");
}
}

方形模板

1
2
3
4
5
6
public class SquareStyle extends CakeStyle {
@Override
public void style() {
System.out.println("Square Style");
}
}

草莓味奶油

1
2
3
4
5
6
public class StrawberryCream extends CakeCream {
@Override
public void cream() {
System.out.println("Strawberry Cream");
}
}

芒果味奶油

1
2
3
4
5
6
public class MangoCream extends CakeCream {
@Override
public void cream() {
System.out.println("Mango Cream");
}
}

现在,无论是材料还是工具都做好了,只要根据需要,通过具体的工厂生产就行了

首先,还是来一个抽象工厂

1
2
3
4
5
6
7
public abstract class CakeFactory {

public abstract CakeCream cream();

public abstract CakeStyle style();
}

然后,就分别是爱心型草莓味蛋糕工厂和方形芒果味蛋糕工厂

爱心型草莓味蛋糕工厂

1
2
3
4
5
6
7
8
9
10
11
12
public class StrawberryHeartCake extends CakeFactory{
@Override
public CakeCream cream() {
return new StrawberryCream();
}

@Override
public CakeStyle style() {
return new HeartStyle();
}
}

方形芒果味蛋糕工厂

1
2
3
4
5
6
7
8
9
10
11
12
public class MangoSquareCake extends CakeFactory {
@Override
public CakeCream cream() {
return new MangoCream();
}

@Override
public CakeStyle style() {
return new SquareStyle();
}
}

大功告成,让我们来看看蛋糕做得怎么样

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
CakeFactory strawberryHeartCake = new StrawberryHeartCake();
strawberryHeartCake.cream().cream();
strawberryHeartCake.style().style();

System.out.println();

CakeFactory mangoSquareCake = new MangoSquareCake();
mangoSquareCake.cream().cream();
mangoSquareCake.style().style();
}

result:

1
2
3
4
5
6
Strawberry Cream
Heart Style

Mango Cream
Square Style

还不赖嘛~

如果,这时需求变更了,比如,现在更流行吃爱心型芒果味蛋糕

那么我们只需要一个工厂就可以做 到了,而不需要再去重头开始制作奶油和模板

爱心型芒果味蛋糕

1
2
3
4
5
6
7
8
9
10
11
public class MangoHeartCake extends CakeFactory {
@Override
public CakeCream cream() {
return new MangoCream();
}

@Override
public CakeStyle style() {
return new HeartStyle();
}
}

还是挺简单的嘛~

好了,来总结一下抽象工厂的优缺点吧

  • 优点:最明显的分离了抽象和实现,基于产品的抽象和实现的分离,是抽象工厂在切换产品类时更加灵活和容易
  • 缺点:就像我们上面看到的,一个产品就需要一个工厂,那么根据产品的越来越多,具体工厂类就越来越多;另一个就是不容易扩展,比如现在有了新的需求,比如蛋糕现在生产时有了磅数(重量)的要求,那么我们就必须去修改抽象工厂类,从而导致所有的具体工厂类都必须修改

END.


源码地址

Author: Inno Fang
Link: http://innofang.github.io/2017/02/25/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%9B%9B-%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-ND 4.0 unless stating additionally.