例子来自于《Head First 设计模式》
工厂模式简单分为三种:简单工厂模式、工厂模式、抽象工厂模式。
简单工厂
简单工厂模式,不像一个设计模式,反而像我们的一种编程习惯。
举个例子。卖pizza。
定义pissa:
public class Pizza {
public void prepare(){};
public void bake(){};
public void cut(){};
public void box(){};
}
pizza店
public class PizzaStore {
public Pizza orderPizza(){
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
我们想要不同的pizza(CheesePizza、GreekPizza、PepperoniPizza,这三个类都继承Pizza),
public class PepperoniPizza extends Pizza {
}
public class GreekPizza extends Pizza {
}
public class CheesePizza extends Pizza {
}
这时目前的代码是不满足的
我们需要对PizzaStore进行修改
public class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if (type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
此代码是根据不同的type创建不同的pizza,
此时,我们想要添加pizza和删除pizza 的时候,发现,需要修改pizzaStore,且如果有多个地方需要创建pizza 的时候,需要重写这些代码,根据面向对象的复用和开闭原则,这里的代码是不友好的。
我们继续设计,将变化的部分与不变的部分隔离开,为变化的部分专门创建一个类SimplePizzaFactory,
public class SimplePizzaFactory {
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if (type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
return pizza;
}
}
PizzaStore
public class PizzaStore {
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;
}
}
这个类,定义了一个CreatePizza()方法,所有客户用这个来实例化对象。这样似乎还是需要修改代码,但是这种设计最大的好处就是可以给多个客户使用,可以复用。
以上就是简单工厂。将会重复使用的代码独立出来,以达到复用的效果,我们平常设计代码就是这么设计的。
简单工厂UML图:
工厂模式
定义:工厂模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工程方法让类把实例化推迟到子类。
还是上面的例子,我么根据新需求进行改进。
目前,pizza店生意太好了,在外地有很多我们的加盟店。但是加盟店根据地方的口味不同pizza的做法也不一样。这时就需要不同的PizzaStore。这时,把PizzaStore设计成工厂。
PizzaStore:
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);
}
PizzaStore当做父类,我们设计几种不同地方的Pizzastore:NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore
我们具体拿NYPizzaStore示例。
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String type) {
if(type.equals("cheese")){
return new NYStyleCheesePizza();
}else if(type.equals("veggie")){
return new NYStyleVeggiePizza();
}else if (type.equals("clam")){
return new NYStyleClamPizza();
}else if (type.equals("pepperoni")){
return new NYStylePepperoniPizza();
}else return null;
}
}
因为父类PizzaStore是抽象类,有抽象方法,所以子类必须实现抽象方法createPizza()。
根据地方口味我们又加了很多口味的pizza(NYStyleCheesePizza,NYStyleVeggiePizza..等)
这里拿NYStyleCheesePizza示例
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";//薄面饼
sauce = "Marinara Sauce";//番茄酱
toppings.add("Grated Reggiano Cheese");//高级奶酪
}
void cut(){
System.out.println("Cutting the pizza into Square slices");//切成正方形
}
}
重写了父类的cut
最后看测试类:
public class PizzaTestDriver {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println(String.format("Ethan ordered a %S",pizza.getName()));
}
}
运行结果:PreparingNY Style Sauce and Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings
Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into Square slices
Place pizza inofficial PizzaStore box
Ethan ordered a NY STYLE SAUCE AND CHEESE PIZZA
我们来自己看看运行流程。创建PizzaStore时,通过多态,创建了NYPizzaStore实例。
用NYPizzaStore实例调用orderPizza(),因为子类NYPizzaStore并没有重写orderPizza(),所以直接调用了父类的orderPizza()方法。
父类的orderPizza()方法调用了抽象方法createPizza(),而createPizza()的实现都在子类中。所以就调用了NYPizzaStore中的createPizza()方法。
就是以上这种运行流程,将具体对象的创建从父类交给了子类。
这就是工厂方法模式。工厂模式通过让子类决定要创建的对象是什么,来达到将对象创建的过程封装的目的。
uml图:
工厂模式结构图:
抽象工厂
定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。还是上面的例子,这次的需求是需要所有的pizza使用的原料是被控制的,
首先原料有Dough面团、Sauce酱、Cheese芝士、Veggies蔬菜、Pepperoni香肠、Clams海蛎
设计原材料工厂:
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Pepperoni createPepperoni();
Clams createClam();
}
各地自己的原材料加工厂,我们以NYPizzaIngreDientFactory为例,且不同的地点需要的材料也不一样。
public 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();
}
@Override
public Veggies[] createVeggies() {
Veggies veggies[] = {new Veggies("西红柿"),new Veggies("生菜"),new Veggies("白菜")};
return veggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return new FreshClams();
}
}
Pizza类:
public abstract class Pizza {
String name;//名称
Dough dough;//面团类型
Sauce sauce;//酱料类型
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam;
abstract void prepare();
void bake(){
System.out.println("Bake for 25 minutes at 350");
};
void cut(){
System.out.println("Cutting the pizza into diagonal slices");
};
void box(){
System.out.println("Place pizza inofficial PizzaStore box");
};
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
}
仔细看虚拟方法prepare();要求所有子类都要重写这个方法!这是完成抽象工厂,让子类去创建相关依赖对象的家族,而不需要明确指定具体类的关键。
继续修改CheesePizza
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
@Override
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
public class ClamPizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
@Override
void prepare() {
System.out.println("Preparing" + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clam = ingredientFactory.createClam();
}
}
目前,Pizza利用相关的工厂生产原料,所生产的原料依赖所使用的工厂,Pizza类根本不关心这些原料,它只知道如何制作pizza。现在Pizza和区域原料之间被解耦。
最后看NYPozzaStore
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String type) {
PizzaIngredientFactory ingredientFactory = new NYPizzaIngreDientFactory();
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New Tork Style Cheese Pizza");
}else if (type.equals("clam")){
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New Tork Style Clam Pizza");
}
return pizza;
}
}
当我们使用NYPizzaStore创建Pizza的时候,原料工厂就会创建对应的原料工厂对象NYPizzaIngreDientFactory,ClamPizza不需要知道用了什么工厂,他直接用对应的方法即可。
uml图