前面我们说了工厂方法模式,当只需要生产某一种类的时候可以使用。如果我们需要生产多类产品,就可以考虑使用抽象工厂模式。
抽象工厂模式的使用情景如下:
- 系统需要多种产品,但是每次只需要一组产品。
- 产品种类较固定,产品的组合可能频繁变化。
说得好像有点云里雾里,所以先看一个例子吧。首先我们先来定义两组产品接口,一个是调料,一个是食物。
代码语言:javascript复制public interface Food {
String getFood();
}
public interface Spice {
String getSpice();
}
然后我们来定义生产食物和调料的抽象工厂。
代码语言:javascript复制public interface AbstractFactory {
Spice getSpice();
Food getFood();
}
然后我们定义两个具体工厂,一个生产蛋糕,一个生产辣条。注意这里使用了Java 8的Lambda表达式。
代码语言:javascript复制public class CakeFactory implements AbstractFactory {
@Override
public Spice getSpice() {
return () -> "糖";
}
@Override
public Food getFood() {
return () -> "蛋糕";
}
}
public class LatiaoFactory implements AbstractFactory {
@Override
public Spice getSpice() {
return () -> "辣椒";
}
@Override
public Food getFood() {
return () -> "辣条";
}
}
然后我们获取一下蛋糕和辣条。可以看到如果需要获取某组产品(糖和蛋糕、辣椒和辣条),那么抽象工厂模式非常适合。而且要创建新的产品组合也很简单,只需要实现一个新的具体工厂即可。
代码语言:javascript复制public void run() {
AbstractFactory factory = new CakeFactory();
Spice spice = factory.getSpice();
Food food = factory.getFood();
printSpiceAndFood(spice, food);
factory = new LatiaoFactory();
spice = factory.getSpice();
food = factory.getFood();
printSpiceAndFood(spice, food);
}
private static void printSpiceAndFood(Spice spice, Food food) {
System.out.println("调料:" spice.getSpice() "," "食物:" food.getFood());
}
比如说我们要创建一个新的咸鸭蛋工厂,也非常简单。
代码语言:javascript复制public class SaltyEggFactory implements AbstractFactory {
@Override
public Spice getSpice() {
return () -> "盐";
}
@Override
public Food getFood() {
return () -> "咸鸭蛋";
}
}
那么抽象工厂模式的缺点是什么呢?其实从上面的例子中我们也可以看出来了。抽象工厂模式创建新的产品组合非常简单(实现新的具体工厂),但是如果我们要增加新的产品种类,代码就需要进行大量更改了。比方说我们现在需要添加一个餐具,那么上面的抽象工厂类、三个具体类的代码就必须全部进行更改。在实际情况中,这往往会破坏代码开发者和使用者之间的协定。
说到这里,抽象工厂模式的优缺点和使用场景也就呼之欲出了。如果系统中需要多种类型的产品,而产品的种类相对固定,不会出现新类型,这时候就可以使用抽象工厂模式。符合这种情景的都可以使用抽象工厂,例如为软件更换界面,由于界面的标题栏、菜单栏等组件相对固定,所以这些可以声明为一个抽象工厂,然后每一中界面样式为一个具体实现工厂。