名称
工厂方法(FACTORY METHOD),别名:虚构造器(Virtual Constructor)
目的
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
适用性
在下列情况下可以使用Factory Method模式:
当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
结构
创造者(Creator):
声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象。
可以调用工厂方法以创建一个Product对象。
具体创造者(ConcreteCreator):重定义工厂方法以返回一个ConcreteProduct实例。
产品(Product):定义工厂方法所创建的对象的接口。
具体产品(ConcreteProduct):实现Product接口。
tips:注意与抽象工厂模式做区分,结构上抽象工厂模式是二维的(产品族、产品),工厂方法模式是一维的(产品),他们的目的也不一样,抽象工厂模式注重于创建于同一系列的产品(同一产品族),工厂方法模式注重于将创建具体产品延迟到具体创建者和连接平行类层次。
协作
Creator依赖于它的子类来定义工厂方法,所以它返回一个适当的ConcreteProduct实例。
效果
优点
为子类提供挂钩(hook)。用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。FactoryMethod给子类一个挂钩以提供对象的扩展版本。
连接平行类层次。工厂方法并不往往只是被Creator调用(还可以是具体创造者),客户可以找到一些有用的工厂方法,当一个类将它的一些职责委托给一个独立的类的时候,就产生了平行类层次(具体产品)。
这句话有点抽象,需要解释一下,可以看到结构图中Creator中有个AnOperation(),每个具体产品都有产品相应的方法实现,这就是平行类层次,在抽象工厂模式中,可以通过AnOperation()进行连接,比如按照特定顺序执行某些方法。这个AnOperation()如果放在Creator中,那就是通过Creator调用(所有具体Creator共用);如果AnOperation()放在具体Creator中,那每个具体Creator可以自定义不一样的AnOperation(),此时就是通过具体Creator调用了。
确保同一产品族的对象一起工作。抽象工厂模式确保了从同一个工厂生产出来的产品属于同一产品族,这保证了产品的兼容性和一致性。
封装变化。当需要改变产品系列时,可以简单地更换不同的具体工厂类,而不需要修改客户端代码,这遵循了“开闭原则”。
易于交换产品系列。抽象工厂模式使得系统更容易切换不同的产品系列,因为所有的产品都是通过一个工厂类创建的。
缺点
产品族扩展困难。如果需要增加一个新的产品族,那么必须修改抽象工厂和所有具体工厂类,这违反了“开闭原则”。
产品等级结构固定。抽象工厂模式要求产品等级结构是固定的,这意味着在一个工厂中不能创建不同产品族的对象。
增加系统复杂度。使用抽象工厂模式会增加系统的复杂度,因为它引入了更多的类和接口。
可能会增加代码量。实现抽象工厂模式通常需要创建多个接口和类,这可能会导致代码量的增加。
应用
问题
在生活中,如果我们去吃披萨,通常会经历以下几个步骤,首先我们会挑选一家披萨店,然后去店里点单,最后店员一顿操作过后,把披萨端上来。在这个过程中,我们去的店不同,每个店的在售披萨种类也不同,同一个店,对于不同披萨的制作流程也会不一样,而这些不一样,我们可以延迟到后面去实现(比如吃什么披萨,延迟到到店后进行挑选,怎么制作,延迟到选定披萨后进行制作),这一点和工厂方法模式的目的是何其相似。披萨店就是创造者,芝加哥披萨店和纽约披萨店就是具体创造者,披萨就是产品,最终选定要吃的披萨就是具体产品。
示例
UML
代码示例
创造者:披萨店
package com.ysj.part3.factoryMethod.creator;
import com.ysj.part3.factoryMethod.product.Pizza;
public abstract class PizzaStore {
/*
* 创建pizza的方法交给子类去实现
*/
public abstract Pizza createPizza(String type);
/**
* 下单披萨
* @param type
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
具体制造者:芝加哥披萨店、纽约披萨店
package com.ysj.part3.factoryMethod.concreteCreator;
import com.ysj.part3.factoryMethod.concreteProduct.ChicagoStyleCheesePizza;
import com.ysj.part3.factoryMethod.concreteProduct.ChicagoStyleClamPizza;
import com.ysj.part3.factoryMethod.concreteProduct.ChicagoStylePepperoniPizza;
import com.ysj.part3.factoryMethod.concreteProduct.ChicagoStyleVeggiePizza;
import com.ysj.part3.factoryMethod.creator.PizzaStore;
import com.ysj.part3.factoryMethod.product.Pizza;
public class ChicagoPizzaStore extends PizzaStore {
public Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new ChicagoStyleCheesePizza();
} else if ("clam".equals(type)) {
pizza = new ChicagoStyleClamPizza();
} else if ("pepperoni".equals(type)) {
pizza = new ChicagoStylePepperoniPizza();
} else if ("veggie".equals(type)) {
pizza = new ChicagoStyleVeggiePizza();
}
return pizza;
}
}
package com.ysj.part3.factoryMethod.concreteCreator;
import com.ysj.part3.factoryMethod.concreteProduct.NYStyleCheesePizza;
import com.ysj.part3.factoryMethod.concreteProduct.NYStyleClamPizza;
import com.ysj.part3.factoryMethod.concreteProduct.NYStylePepperoniPizza;
import com.ysj.part3.factoryMethod.concreteProduct.NYStyleVeggiePizza;
import com.ysj.part3.factoryMethod.creator.PizzaStore;
import com.ysj.part3.factoryMethod.product.Pizza;
public class NYPizzaStore extends PizzaStore {
public Pizza createPizza(String item) {
Pizza pizza = null;
if ("cheese".equals(item)) {
pizza = new NYStyleCheesePizza();
} else if ("veggie".equals(item)) {
pizza = new NYStyleVeggiePizza();
} else if ("clam".equals(item)) {
pizza = new NYStyleClamPizza();
} else if ("pepperoni".equals(item)) {
pizza = new NYStylePepperoniPizza();
}
return pizza;
}
}
产品:披萨,使用抽象类,几个方法是缺省(默认)方法,子类根据需要进行重写,不重写使用的就是缺省方法,此处使用抽象类比使用结构扩展性更强,不必重写所有方法
package com.ysj.part3.factoryMethod.product;
import java.util.ArrayList;
import java.util.List;
public abstract class Pizza {
protected String name; //名称
protected String dough; //面团
protected String sause; //酱料
protected List<String> toppings = new ArrayList<String>(); //佐料
public void prepare() {
System.out.println("准备 "+name);
System.out.println("活面团");
System.out.println("加入酱汁");
System.out.println("加入作料:");
for(int i = 0;i < toppings.size();i++){
System.out.println(" "+toppings.get(i));
}
}
public void bake() {
System.out.println("在350度下烤25分钟");
}
public void cut() {
System.out.println("把披萨切成对角线的薄片");
}
public void box() {
System.out.println("把披萨放在官方的披萨店盒子里");
}
public String getName(){
return name;
}
}
具体产品:芝加哥风和纽约风的奶酪披萨、蛤蜊披萨、辣香肠披萨、辣素食披萨,这里芝加哥和纽约的披萨可以不对称
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza() {
name = "芝加哥风奶酪披萨";
dough = "芝加哥面团";
sause = "芝加哥酱料";
toppings.add("辣椒粉");
toppings.add("辣椒酱");
}
public void prepare() {
System.out.println("准备 " + name);
System.out.println("活面团");
System.out.println("加入酱汁");
System.out.println("加入" + sause);
System.out.println("加入作料:");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}
public void bake() {
System.out.println("在250温度下烤35分钟");
}
public void cut() {
System.out.println("把披萨切成正方形");
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class ChicagoStyleClamPizza extends Pizza {
public ChicagoStyleClamPizza() {
name = "芝加哥风蛤蜊披萨";
dough = "芝加哥面团";
sause = "芝加哥酱料";
toppings.add("辣椒酱");
}
public void prepare() {
System.out.println("准备 " + name);
System.out.println("活面团");
System.out.println("加入酱汁");
System.out.println("加入作料:");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class ChicagoStylePepperoniPizza extends Pizza {
public ChicagoStylePepperoniPizza() {
name = "芝加哥辣香肠披萨";
dough = "芝加哥面团";
sause = "芝加哥酱料";
toppings.add("胡椒粉");
}
public void box() {
System.out.println("把披萨放在官方的芝加哥披萨店盒子里");
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class ChicagoStyleVeggiePizza extends Pizza {
public ChicagoStyleVeggiePizza() {
name = "芝加哥辣素食披萨";
dough = "芝加哥面团";
sause = "芝加哥酱料";
toppings.add("胡椒粉");
}
public void cut() {
System.out.println("切成三角形");
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza(){
name = "纽约风奶酪披萨";
dough = "纽约面团";
sause = "纽约酱料";
toppings.add("番茄酱");
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza(){
name = "纽约风奶酪披萨";
dough = "纽约面团";
sause = "纽约酱料";
toppings.add("番茄酱");
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class NYStyleClamPizza extends Pizza {
public NYStyleClamPizza() {
name = "纽约风蛤蜊披萨";
dough = "纽约面团";
sause = "纽约酱料";
toppings.add("辣椒酱");
}
public void prepare() {
System.out.println("准备 " + name);
System.out.println("活面团");
System.out.println("加入酱汁");
System.out.println("加入作料:");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}
}
package com.ysj.part3.factoryMethod.concreteProduct;
import com.ysj.part3.factoryMethod.product.Pizza;
public class NYStylePepperoniPizza extends Pizza {
public NYStylePepperoniPizza() {
name = "纽约辣香肠披萨";
dough = "纽约面团";
sause = "纽约酱料";
toppings.add("胡椒粉");
}
public void box() {
System.out.println("把披萨放在官方的纽约披萨店盒子里");
}
}
客户端:吃披萨的人
package com.ysj.part3.factoryMethod;
import com.ysj.part3.factoryMethod.concreteCreator.NYPizzaStore;
import com.ysj.part3.factoryMethod.concreteCreator.ChicagoPizzaStore;
import com.ysj.part3.factoryMethod.creator.PizzaStore;
import com.ysj.part3.factoryMethod.product.Pizza;
/**
* 工厂方法模式
* 定义一个用于创建对象的接口,让子类决定实例化哪一个类。 Factory Method使一个类的实例化延迟到其子类
*/
public class Client {
public static void main(String[] args) {
PizzaStore chicagoPizzaStore = new ChicagoPizzaStore(); //建立芝加哥的披萨店
Pizza joelPizza = chicagoPizzaStore.orderPizza("cheese"); //下订单
System.out.println("Joel 得到一份 " + joelPizza.getName() + "\n");
PizzaStore nyPizzaStore = new NYPizzaStore();
Pizza ethanPizza = nyPizzaStore.orderPizza("veggie");
System.out.println("Ethan 得到一份 " + ethanPizza.getName());
}
}
已知应用
mybatis
sql会话工厂SqlSessionFactory的创建、数据源DataSource的创建都使用了工厂方法模式。
评论区