工厂设计模式是一种广泛使用的创建型设计模式,它的核心思想是定义一个用于创建对象的接口,让子类决定实例化哪一个类。这种模式可以使代码更具灵活性和可扩展性,尤其在面对复杂对象结构和大量类需要实例化时,能够降低耦合度和提高程序的组织结构。
工厂模式主要包括以下几种实现方式:
1、简单工厂模式(Simple Factory Pattern / Static Factory Method)
在Java中实现简单工厂模式(Simple Factory Pattern),我们通常创建一个工厂类,该类包含静态方法来决定并创建所需的对象。以下是一个简单工厂模式的示例,我们将创建一个披萨店,可以制作多种口味的披萨:
首先,我们定义一个抽象披萨类(Pizza):
public abstract class Pizza {
public abstract void prepare();
public abstract void bake();
public abstract void cut();
public abstract void box();
public void deliver() {
System.out.println("Delivering the pizza now...");
}
}
然后,我们创建几种具体口味的披萨类继承自抽象披萨类:
public class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("Preparing cheese pizza...");
}
// ... 实现其他方法
}
public class PepperoniPizza extends Pizza {
@Override
public void prepare() {
System.out.println("Preparing pepperoni pizza...");
}
// ... 实现其他方法
}
接下来,创建一个披萨工厂类(PizzaStore):
public class PizzaStore {
public static Pizza orderPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
} else {
System.out.println("Sorry, we don't make that kind of pizza yet.");
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
最后,客户端代码可以通过工厂类下单并制作披萨:
public class Client {
public static void main(String[] args) {
Pizza pizza = PizzaStore.orderPizza("cheese");
pizza.deliver();
pizza = PizzaStore.orderPizza("pepperoni");
pizza.deliver();
}
}
在这个例子中,PizzaStore
类就是简单工厂,它根据传入的类型参数("cheese" 或 "pepperoni")创建相应的披萨对象。客户端只需调用 orderPizza
方法,无需了解具体的披萨制作过程。
优点:隐藏了创建产品的具体逻辑,简化了客户端代码。
缺点:违背开闭原则,每当增加新产品时需要修改工厂类的代码
2、工厂方法模式(Factory Method Pattern)
定义一个工厂接口(或抽象类),在其子类中实现具体的产品创建逻辑。 客户端通过调用工厂接口的通用方法来获得所需的产品对象。
以下是工厂方法模式的一个简单示例,我们还是以披萨店为例,这次我们将创建一个抽象披萨店,具体披萨店负责制作不同口味的披萨:
首先,我们保留之前的抽象披萨类(Pizza)不变:
public abstract class Pizza {
public abstract void prepare();
public abstract void bake();
public abstract void cut();
public abstract void box();
public void deliver() {
System.out.println("Delivering the pizza now...");
}
}
然后,我们仍然有各种口味的具体披萨类:
public class CheesePizza extends Pizza {
// ... 实现方法
}
public class PepperoniPizza extends Pizza {
// ... 实现方法
}
接下来,我们创建一个抽象披萨店类(AbstractPizzaStore),包含一个工厂方法:
public abstract class AbstractPizzaStore {
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);
}
接着,我们创建具体披萨店类继承自抽象披萨店类,并实现工厂方法:
public class NYPizzaStore extends AbstractPizzaStore {
@Override
protected Pizza createPizza(String type) {
if ("cheese".equals(type)) {
return new NYCheesePizza();
} else if ("pepperoni".equals(type)) {
return new NYPepperoniPizza();
} else {
return null;
}
}
}
// 类似地,可以创建ChicagoPizzaStore,对应芝加哥风味的披萨
最后,客户端代码通过具体披萨店下单并制作披萨:
public class Client {
public static void main(String[] args) {
AbstractPizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
pizza.deliver();
pizza = nyStore.orderPizza("pepperoni");
pizza.deliver();
}
}
在这个例子中,NYPizzaStore
类实现了 AbstractPizzaStore
的 createPizza
方法,即工厂方法,根据传入的披萨类型创建相应的披萨对象。这样,每个具体的披萨店子类都可以定制自己的披萨品种,而抽象披萨店则负责统一的订单流程。
优点:很好地支持了开闭原则,扩展新产品时无需修改原有的工厂类,只需要新增一个实现工厂接口的子类。
缺点:随着产品类的增多,可能会有大量的工厂类。
3、抽象工厂模式(Abstract Factory Pattern)
提供一个接口,用于创建相关或依赖对象家族的一系列相关或相互依赖的对象,而不指定具体的产品类。适合于一个产品族内的多个产品对象都需要一起创建的情况。
以下是抽象工厂模式的一个简单示例,我们继续沿用披萨店的例子,这次不仅要生产不同口味的披萨,还要区分纽约风格和芝加哥风格的披萨原料:
首先,我们依然保留抽象披萨类(Pizza)和具体口味的披萨类:
public abstract class Pizza {
// ... 共享的方法实现
}
public class NYStyleCheesePizza extends Pizza {
// ... 纽约风格奶酪披萨特有的实现
}
public class ChicagoStyleCheesePizza extends Pizza {
// ... 芝加哥风格奶酪披萨特有的实现
}
// 同样地,创建其他具体口味的披萨类
然后,我们创建一个抽象原料工厂接口:
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
// ... 其他原料的创建方法
}
接下来,我们创建两个具体原料工厂类,分别对应纽约风格和芝加哥风格:
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough(); // 假设纽约风格披萨用薄底面团
}
// ... 实现其他原料的创建方法
}
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new DeepDishDough(); // 假设芝加哥风格披萨用深盘面团
}
// ... 实现其他原料的创建方法
}
最后,我们创建一个抽象披萨店类,并关联原料工厂:
public abstract class AbstractPizzaStore {
protected PizzaIngredientFactory ingredientFactory;
public AbstractPizzaStore(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
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);
}
再创建具体披萨店类,实现抽象披萨店的 createPizza
方法,并关联相应的原料工厂:
public class NYPizzaStore extends AbstractPizzaStore {
public NYPizzaStore() {
super(new NYPizzaIngredientFactory());
}
@Override
protected Pizza createPizza(String type) {
if ("cheese".equals(type)) {
return new NYStyleCheesePizza(ingredientFactory);
} // ... 其他口味的披萨创建
}
}
// 同样创建ChicagoPizzaStore类
客户端代码通过具体披萨店下单并制作披萨,同时隐含选择了原料工厂:
public class Client {
public static void main(String[] args) {
AbstractPizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
pizza.deliver();
// 创建芝加哥风格披萨店并下单
// ...
}
}
在这个例子中,抽象工厂模式允许客户端通过选择不同的披萨店(即具体工厂)来决定生产何种风格的披萨(产品),同时披萨的原料也是由选定的工厂提供的,体现了工厂之间的关联和产品族的概念。
优点:更加灵活,有助于分隔组件的独立性和减少依赖关系,方便更换整体的产品族。
缺点:增加了系统的复杂度,更多的类和接口需要维护。
工厂模式的主要用途在于解耦,使得代码更容易维护和扩展,可以根据配置信息或其他条件灵活地创建不同类型的对象,而客户端不必知道具体的实现细节。在实际开发中,工厂模式常常与其他设计模式相结合,以更好地适应复杂的设计需求。