需求
开披萨店,披萨有很多种,披萨店也可以有很多个分店
目的
对修改关闭,针对接口编程,不针对实现编程
简单工厂
披萨
public abstract class Pizza
{
String name;
String dough;// 面团
String sauce;// 酱
ArrayList<String> toppings = new ArrayList<String>();// 浇在披萨上的
public String getName()
{
return name;
}
// 准备
public void prepare()
{
System.out.println("Prepare " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (String topping : toppings)
{
System.out.println(" " + topping);
}
}
// 烘烤
public void bake()
{
System.out.println("Baking " + name);
}
// 切片
public void cut()
{
System.out.println("Cutting " + name);
}
// 打包
public void box()
{
System.out.println("Boxing " + name);
}
public String toString()
{
// 显示披萨名称和配料
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(dough + "\n");
display.append(sauce + "\n");
for (String topping : toppings)
{
display.append(topping + "\n");
}
return display.toString();
}
}
// ------------------------------------------------------------------------
// 蛤蜊披萨
public class ClamPizza extends Pizza
{
public ClamPizza()
{
name = "Clam Pizza";
dough = "Thin crust";
sauce = "White garlic sauce";
toppings.add("Clams");
toppings.add("Grated parmesan cheese");
}
}
<br />
<br />
披萨店
public class PizzaStore
{
SimplePizzaFactory factory; // 披萨店接收一个披萨制造工厂
public PizzaStore(SimplePizzaFactory factory)
{
this.factory = factory;
}
// 客户可以订披萨
public Pizza orderPizza(String type)
{
Pizza pizza;
pizza = factory.createPizza(type); // 交给工厂来创建披萨
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
<br />
<br />
披萨工厂
public class SimplePizzaFactory
{
public Pizza createPizza(String type)
{
Pizza pizza = null;
if (type.equals("cheese"))
{
pizza = new CheesePizza();
} else if (type.equals("pepperoni"))
{
pizza = new PepperoniPizza();
} else if (type.equals("clam"))
{
pizza = new ClamPizza();
} else if (type.equals("veggie"))
{
pizza = new VeggiePizza();
}
return pizza;
}
}
<br />
<br />
测试
public static void main(String[] args)
{
// 创建个工厂
SimplePizzaFactory factory = new SimplePizzaFactory();
// 创建个披萨店
PizzaStore store = new PizzaStore(factory);
// 预订披萨,披萨店使用工厂创建披萨,然后披萨店自己处理披萨的烘烤、切片、装盒
Pizza pizza = store.orderPizza("cheese");
System.out.println("We ordered a " + pizza.getName() + "\n");
System.out.println(pizza);
pizza = store.orderPizza("veggie");
System.out.println("We ordered a " + pizza.getName() + "\n");
System.out.println(pizza);
}
简单工厂的问题和好处
- 把创建披萨的代码放到了一个地方,SimplePizzaFactory的客户可以有很多,不只是orderPizza,可以是菜单什么的,通过工厂获取价格
- 可以使用静态方法来替代工厂,成为静态工厂,但是缺点是没法通过继承来改变行为
<br />
<br />
工厂方法,重做披萨店
由于很多加盟商,在不同的地区开店,因此需要配合当地的口味,所以需要定制披萨店
披萨店重做
public abstract class PizzaStore
{
// 这个方法就像一个工厂,由子类决定如何制造披萨
public abstract Pizza createPizza(String item);
public Pizza orderPizza(String type)
{
// 创建披萨,由子类来决定,你要创建的是什么样的披萨
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
// 加工,由于披萨的风味和制作方法各有不同,因此也交给子类来决定,从而达到解耦的目的
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
// 开店
public class ChicagoPizzaStore extends PizzaStore
{
public Pizza createPizza(String item)
{
if (item.equals("cheese"))
{
return new ChicagoStyleCheesePizza();
} else if (item.equals("veggie"))
{
return new ChicagoStyleVeggiePizza();
} else if (item.equals("clam"))
{
return new ChicagoStyleClamPizza();
} else if (item.equals("pepperoni"))
{
return new ChicagoStylePepperoniPizza();
} else
return null;
}
}
披萨可以自己定义加工自己的方式
public class ChicagoStyleClamPizza extends Pizza
{
public ChicagoStyleClamPizza()
{
name = "Chicago Style Clam Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
toppings.add("Frozen Clams from Chesapeake Bay");
}
public void cut()
{
System.out.println("Cutting the pizza into square slices");
}
}
工厂方法模式的定义
定义了一个创建对象的接口,由子类决定要创建的对象是哪一个,工厂方法让类实例化推迟到了子类中。
设计原则 6 :依赖倒置原则
要依赖抽象,不要依赖具体类。
不能让高层组件依赖于具体的低层组件,在这个例子中,披萨店就是高层,披萨就是低层,而不管对于高层还是低层,都应该依赖于抽象,而不要依赖于具体的抽象类。
比如上面的PizzaStore依赖的就是Pizza这个抽象,而不是依赖于具体的某个Pizza子类
如何应用此原则
- 变量不可以持有具体类的引用,比如使用new得到具体类的实例,可以使用工厂方法
- 类不可以派生于具体类,需要派生于一个抽象,就是接口或抽象类
- 不要覆盖父类中已实现的方法,父类中的方法应该被所有的子类共享
<br />
<br />
抽象工厂
现在不仅披萨店要定制,制作披萨的原料也要定制,因此需要不同的原料工厂
原料工厂
// 原料工厂,每种原料都对应一种创建的方法,抽象化,交给子类决定使用什么原料
public interface PizzaIngredientFactory
{
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
// 芝加哥原料工厂,指定要使用哪些原料
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory
{
public Dough createDough()
{
return new ThickCrustDough();
}
public Sauce createSauce()
{
return new PlumTomatoSauce();
}
public Cheese createCheese()
{
return new MozzarellaCheese();
}
public Veggies[] createVeggies()
{
Veggies veggies[] =
{ new BlackOlives(), new Spinach(), new Eggplant() };
return veggies;
}
public Pepperoni createPepperoni()
{
return new SlicedPepperoni();
}
public Clams createClam()
{
return new FrozenClams();
}
}
原料接口,同一种原料的生产之类的可能每个地区都不一样
public interface Cheese
{
public String toString();
}
public class MozzarellaCheese implements Cheese
{
public String toString()
{
return "Shredded Mozzarella";
}
}
// 省略其他原料定义
披萨
public abstract class Pizza
{
public String name;
public Dough dough;
public Sauce sauce;
public Veggies veggies[];
public Cheese cheese;
public Pepperoni pepperoni;
public Clams clam;
public abstract void prepare();
public void bake()
{
System.out.println("Bake for 25 minutes at 350");
}
public void cut()
{
System.out.println("Cutting the pizza into diagonal slices");
}
public void box()
{
System.out.println("Place pizza in official PizzaStore box");
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public String toString()
{
StringBuffer result = new StringBuffer();
result.append("---- " + name + " ----\n");
if (dough != null)
{
result.append(dough);
result.append("\n");
}
if (sauce != null)
{
result.append(sauce);
result.append("\n");
}
if (cheese != null)
{
result.append(cheese);
result.append("\n");
}
if (veggies != null)
{
for (int i = 0; i < veggies.length; i++)
{
result.append(veggies[i]);
if (i < veggies.length - 1)
{
result.append(", ");
}
}
result.append("\n");
}
if (clam != null)
{
result.append(clam);
result.append("\n");
}
if (pepperoni != null)
{
result.append(pepperoni);
result.append("\n");
}
return result.toString();
}
}
public class CheesePizza extends Pizza
{
PizzaIngredientFactory ingredientFactory;
// 由商店决定要用哪个工厂的原料
public CheesePizza(PizzaIngredientFactory ingredientFactory)
{
this.ingredientFactory = ingredientFactory;
}
public void prepare()
{
System.out.println("Preparing " + name);
// 只要是工厂就可以,不在乎具体是谁
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
披萨店
父类不需要更改了,由子类自己决定自己的店需要的原料工厂
public class ChicagoPizzaStore extends PizzaStore
{
protected Pizza createPizza(String item)
{
Pizza pizza = null;
// 指定工厂
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
if (item.equals("cheese"))
{
pizza = new CheesePizza(ingredientFactory);
pizza.setName("Chicago Style Cheese Pizza");
} else if (item.equals("veggie"))
{
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("Chicago Style Veggie Pizza");
} else if (item.equals("clam"))
{
pizza = new ClamPizza(ingredientFactory);
pizza.setName("Chicago Style Clam Pizza");
} else if (item.equals("pepperoni"))
{
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("Chicago Style Pepperoni Pizza");
}
return pizza;
}
}
抽象工厂的定义
提供一个接口,用于创建相关的,或者依赖的家族,而不需要明确指定具体类
工厂方法和抽象工厂的比较
抽象工厂的方法,经常以工厂方法的方式呈现
工厂方法使用的是继承,继承一个工厂方法去创建对象,客户只需要知道它的父抽象类型即可。抽象工厂使用的是组合,在继承到方法创建对象后,然后将相关的产品组合起来.
抽象工厂由于是大的产品家族,后续要是加入新的类型,就需要更改接口,工作量会很大
总之,需要产品族的用抽象工厂,如果只是单一的,可以用工厂方法