工厂设计模式:打造你的代码生产线
引言
想象一下,你正站在一家现代化的玩具工厂门前。工厂内部,各种机器有条不紊地运转,原材料在传送带上流动,最终变成精美的玩具。你不需要了解每个玩具的具体制作工艺,只需告诉工厂经理:“我需要一个泰迪熊”,工厂就会为你生产出来。这正是工厂设计模式的核心思想 — 封装对象的创建过程,使客户端代码与具体产品的实现细节解耦。
什么是工厂设计模式?
工厂设计模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指向新创建的对象。
工厂模式主要分为三种类型:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
简单工厂模式:披萨店的点单系统
场景描述
想象你走进了一家披萨店。你不需要自己准备面团、酱料和配料,只需告诉服务员你想要什么类型的披萨(如"奶酪披萨"、“素食披萨"或"肉食披萨”),服务员就会将你的订单传递给厨房,厨房(工厂)将为你制作披萨。
代码实现
// 披萨接口
interface Pizza {
void prepare();
void bake();
void cut();
void box();
}
// 具体披萨类 - 奶酪披萨
class CheesePizza implements Pizza {
@Override
public void prepare() {
System.out.println("准备奶酪披萨的原料...");
}
@Override
public void bake() {
System.out.println("烘烤奶酪披萨...");
}
@Override
public void cut() {
System.out.println("切割奶酪披萨...");
}
@Override
public void box() {
System.out.println("包装奶酪披萨...");
}
}
// 具体披萨类 - 素食披萨
class VeggiePizza implements Pizza {
@Override
public void prepare() {
System.out.println("准备素食披萨的原料...");
}
@Override
public void bake() {
System.out.println("烘烤素食披萨...");
}
@Override
public void cut() {
System.out.println("切割素食披萨...");
}
@Override
public void box() {
System.out.println("包装素食披萨...");
}
}
// 简单工厂
class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
}
// 可以添加更多披萨类型...
return pizza;
}
}
// 披萨店类
class PizzaStore {
private SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
// 客户端代码
public class SimplePizzaExample {
public static void main(String[] args) {
SimplePizzaFactory factory = new SimplePizzaFactory();
PizzaStore store = new PizzaStore(factory);
// 订购奶酪披萨
Pizza cheesePizza = store.orderPizza("cheese");
System.out.println("小明订购了一个奶酪披萨\n");
// 订购素食披萨
Pizza veggiePizza = store.orderPizza("veggie");
System.out.println("小红订购了一个素食披萨");
}
}
简单工厂模式的优缺点
优点:
- 客户端不需要知道具体产品类的类名,只需知道对应的参数即可
- 将创建实例的过程与使用实例的过程分离,使用者不必关心创建的细节
缺点:
- 工厂类集中了所有产品的创建逻辑,一旦工厂类出现问题,整个系统都会受到影响
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,违反了开闭原则
工厂方法模式:特色地区披萨店连锁
场景描述
随着业务扩展,披萨店开始在不同城市开设分店。每个地区的分店都有自己的特色披萨。比如,纽约店的披萨饼皮薄而脆,而芝加哥店的披萨饼皮厚实松软。这时候,简单工厂已经不能满足需求,需要让各个分店能够创建自己特色的披萨。
代码实现
// 披萨接口
interface Pizza {
void prepare();
void bake();
void cut();
void box();
String getName();
}
// 纽约风格奶酪披萨
class NYStyleCheesePizza implements Pizza {
private String name = "纽约风格奶酪披萨";
@Override
public void prepare() {
System.out.println("准备 " + name);
}
@Override
public void bake() {
System.out.println("烘烤25分钟");
}
@Override
public void cut() {
System.out.println("对角线切片");
}
@Override
public void box() {
System.out.println("放入纽约披萨店盒子中");
}
@Override
public String getName() {
return name;
}
}
// 芝加哥风格奶酪披萨
class ChicagoStyleCheesePizza implements Pizza {
private String name = "芝加哥风格深盘奶酪披萨";
@Override
public void prepare() {
System.out.println("准备 " + name);
}
@Override
public void bake() {
System.out.println("烘烤40分钟");
}
@Override
public void cut() {
System.out.println("方块切片");
}
@Override
public void box() {
System.out.println("放入芝加哥披萨店盒子中");
}
@Override
public String getName() {
return name;
}
}
// 抽象披萨店
abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 工厂方法
protected abstract Pizza createPizza(String type);
}
// 纽约披萨店
class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new NYStyleCheesePizza();
}
// 可以添加更多纽约风格披萨...
return null;
}
}
// 芝加哥披萨店
class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new ChicagoStyleCheesePizza();
}
// 可以添加更多芝加哥风格披萨...
return null;
}
}
// 客户端代码
public class FactoryMethodExample {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("小明订购了 " + pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("小红订购了 " + pizza.getName());
}
}
工厂方法模式的优缺点
优点:
- 符合开闭原则,新增产品时,只需添加具体工厂和产品类,不需要修改原有代码
- 符合单一职责原则,每个具体工厂只负责创建对应的产品
- 客户端只需要知道产品的抽象类,无须关心具体实现类
缺点:
- 类的个数容易过多,增加复杂度
- 增加了系统的抽象性和理解难度
抽象工厂模式:披萨原料工厂
场景描述
随着披萨连锁店的成功,管理层决定统一原料供应链,确保每家分店都使用高质量的原料。不同地区的披萨店需要使用不同风格的原料(如纽约的薄面团和马苏里拉奶酪,芝加哥的厚面团和意大利白干酪)。
代码实现
// 原料接口
interface Dough {
String toString();
}
interface Sauce {
String toString();
}
interface Cheese {
String toString();
}
// 具体原料类
class ThinCrustDough implements Dough {
public String toString() {
return "薄饼皮";
}
}
class ThickCrustDough implements Dough {
public String toString() {
return "厚饼皮";
}
}
class MarinaraSauce implements Sauce {
public String toString() {
return "番茄酱";
}
}
class PlumTomatoSauce implements Sauce {
public String toString() {
return "李子番茄酱";
}
}
class MozzarellaCheese implements Cheese {
public String toString() {
return "马苏里拉奶酪";
}
}
class ReggianoCheese implements Cheese {
public String toString() {
return "雷吉亚诺奶酪";
}
}
// 抽象原料工厂
interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
// 可以添加更多原料...
}
// 具体原料工厂 - 纽约
class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
}
// 具体原料工厂 - 芝加哥
class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThickCrustDough();
}
@Override
public Sauce createSauce() {
return new PlumTomatoSauce();
}
@Override
public Cheese createCheese() {
return new MozzarellaCheese();
}
}
// 抽象产品 - 披萨
abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Cheese cheese;
abstract void prepare();
void bake() {
System.out.println("烘烤175度,25分钟");
}
void cut() {
System.out.println("将披萨对角切片");
}
void box() {
System.out.println("将披萨放在官方盒子里");
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
public String toString() {
StringBuilder result = new StringBuilder();
result.append("---- ").append(name).append(" ----\n");
if (dough != null) {
result.append(dough).append("\n");
}
if (sauce != null) {
result.append(sauce).append("\n");
}
if (cheese != null) {
result.append(cheese);
}
return result.toString();
}
}
// 具体产品 - 奶酪披萨
class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
@Override
void prepare() {
System.out.println("准备 " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
// 改进后的披萨店
abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
// 纽约披萨店
class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (type.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("纽约风格奶酪披萨");
}
// 可以添加更多纽约风格披萨...
return pizza;
}
}
// 芝加哥披萨店
class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
if (type.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("芝加哥风格奶酪披萨");
}
// 可以添加更多芝加哥风格披萨...
return pizza;
}
}
// 客户端代码
public class AbstractFactoryExample {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("小明订购了:\n" + pizza + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("小红订购了:\n" + pizza);
}
}
抽象工厂模式的优缺点
优点:
- 产品族的约束性,确保一系列相关的产品对象被一起创建
- 将一个系列的产品族统一到一起创建,减少了具体产品对象的依赖
- 符合开闭原则,易于扩展,可以在不修改已有代码的情况下引入新的产品族
缺点:
- 扩展产品族比较困难,需要修改抽象工厂的接口
- 比工厂方法模式更加抽象,更难理解
工厂模式在实际开发中的应用
工厂模式在许多框架和应用中被广泛使用:
Java集合框架:
Collections.singleton()
、Collections.singletonList()
等方法都是工厂方法的实现。JDBC连接数据库:通过
DriverManager.getConnection()
方法获取数据库连接对象,隐藏了具体数据库连接的细节。Spring框架:Spring的BeanFactory是工厂模式的典型应用,它根据配置文件创建不同的Bean对象。
日志框架:如Log4j、SLF4J等,它们使用工厂模式创建不同的日志对象。
总结
工厂设计模式是一种非常实用的创建型设计模式,它通过封装对象的创建过程,使客户端代码与具体产品的实现细节解耦。根据不同的需求和场景,我们可以选择使用简单工厂、工厂方法或抽象工厂。
- 简单工厂:适用于产品种类相对固定,变化不大的场景。
- 工厂方法:适用于产品种类多样化,需要不同工厂创建不同产品的场景。
- 抽象工厂:适用于需要创建一系列相关或依赖对象的场景,无须指定它们的具体类。