4.工厂模式
工厂模式分为工厂方法模式和抽象工厂模式
(1).工厂方法模式:
定义:
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
示例:
你的Pizza店想在全国开加盟店。你想每家加盟店都能根据区域的差异提供不同风味的比萨,但同时你也希望建立一个框架,使加盟店能按照你既定的整个订单系统生产pizza(同样的烘烤工艺,同样的切片方式以及使用同样厂商的盒子)。
未达到这个目的,可以先创意一个抽象的超类PizzaStore,超类中有方法orderPizza()实现了整个订单系统的流程,有一个抽象方法createPizza()定义了Pizza的具体产生。两个函数的关系式orderPizza()将调用createPizza()。
下面,只要让各地区的加盟店继承超类,并实现自己的生产Pizza的方法createPizza()。这样就可以让各加盟店生产出自己地区风味的pizza而又遵守了整个的订单流程。
类图如下:
当orderPizza()调用createPizza()时,某个比萨店子类将负责创建比萨。做哪一种比萨将由具体的比萨店子类来决定。
代码:
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza;
pizza=createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
public class NYPizzaStore extends PizzaStore {
public Pizza createPizza(String type){
if(type.equals("cheese")){
return new NYStyleCheesePizza();
} //还可以根据需要返回不同的Pizza
else return null;
}
}
public class ChicagoPizzaStore extends PizzaStore {
public Pizza createPizza(String type){
if(type.equals("cheese")){
return new ChicagoStyleCheesePizza();
}//还可以根据需要返回不同的Pizza
else return null;
}
}
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList<String> toppings = new ArrayList<String>();
void prepare() {
System.out.println("Preparing" + name);
System.out.println("Tossing daugh...");
System.out.println("Adding sauce...");
for (String s : toppings) {
System.out.println(" " + s);
}
}
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 puzza in official PizzaStore box");
}
public String getName(){
return name;
}
}
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");
}
}
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza(){
name="Chicage Style Deep Dish Cheese Pizza";
dough="Extra Thick Crust Dough";
sauce="Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
测试:public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore=new NYPizzaStore();
PizzaStore chicagoStore=new ChicagoPizzaStore();
Pizza pizza=nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a "+pizza.getName()+"\n");
pizza=chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a "+pizza.getName()+"\n");
}
}
结果:PreparingNY Style Sauce and Cheese Pizza
Tossing daugh...
Adding sauce...
Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place puzza in official PizzaStore box
Ethan ordered a NY Style Sauce and Cheese Pizza
PreparingChicage Style Deep Dish Cheese Pizza
Tossing daugh...
Adding sauce...
Shredded Mozzarella Cheese
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place puzza in official PizzaStore box
Joel ordered a Chicage Style Deep Dish Cheese Pizza
在PizzaStore中,
protected abstract Pizza createPizza(String type);
负责实例化比萨的责任,此方法就如同是一个“工厂”。
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。
对工厂方法的解读:
abstract Product factoryMethod(String type)
abstract:工厂方法是抽象的,所以依赖子类来处理对象的创建。
Product:工厂方法必须返回一个产品,通常使用到工厂方法的返回值。
String type 工厂方法可能需要参数来指定所要的产品。
解读刚才的类图:
工厂方法模式的一般类图:
工厂方法模式好处:
工厂方法让子类决定要实例化的类是哪一个。所谓的“决定”,并不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定了实际创建的产品是什么。
将创建对象的代码集中在一个对象或方法中,可以避免代码中的重复,并且更方便以后的维护。这也意味着客户在实例化对象时,只会依赖与接口,而不是具体类。这可以帮我们针对接口编程,而不针对实现编程。这让代码更具有弹性,可以应对未来的扩展。
(2).抽象工厂模式:
定义:
提供一个借口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
示例:虽然现在各加盟店都统一采用了你的框架,但是有一些加盟店使用了低价原料来增加利润。为了要确保每家加盟店使用高质量的原料,你打算建造一家生产原料的工厂,并将原料运送到各家加盟店。但是有一个问题,加盟店坐落在不同的区域,每个区域的同类原料并不相同,比如纽约的和芝加哥的红酱料并不相同。所以即使你有相同的产品家族,但其制作方式也会根据区域的不同而有差异。
现在先建造一个工厂来生产原料,这个工厂负责生产原料家族中的每一种原料。
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
//每个原料都是一个类
}
为纽约的加盟店建造纽约的原料工厂:public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
// TODO Auto-generated method stub
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
// TODO Auto-generated method stub
return new marinaraSauce();
}
@Override
public Cheese createCheese() {
// TODO Auto-generated method stub
return new ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
// TODO Auto-generated method stub
Veggies[] veggies={new Garlic(),new Onion(),new Mushroom(),new RedPepper()}
return veggies;
}
@Override
public Pepperoni createPepperoni() {
// TODO Auto-generated method stub
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
// TODO Auto-generated method stub
return new FreshClams();
}
}
为芝加哥的加盟店建造芝加哥的原料工厂:
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
// TODO Auto-generated method stub
return new ThickCrustDough();
}
@Override
public Sauce createSauce() {
// TODO Auto-generated method stub
return new PlumTomatoSauce();
}
@Override
public Cheese createCheese() {
// TODO Auto-generated method stub
return new Mozzarella();
}
@Override
public Veggies[] createVeggies() {
// TODO Auto-generated method stub
Veggies[] veggies={new BlackOlives(),new Spinach(),new EggPlant()}
return veggies;
}
@Override
public Pepperoni createPepperoni() {
// TODO Auto-generated method stub
return new SlicedPepperoni;
}
@Override
public Clams createClam() {
// TODO Auto-generated method stub
return new FrozenClams();
}
}
现在重新定义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 puzza in official PizzaStore box");
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public String toString(){
//这里是打印Pizza的代码
}
}
虽然之前写了NYCheesePizza和ChicagoCheesePizza类,但比较一下这两个类,唯一的却别在于使用区域性的原料,至于比萨的做法都一样,其他的比萨也是如此,它们都依赖着相同的准备步骤,只是使用不同的原料。
其实我们不需要设计两个不同的类来处理不同风味的比萨,让原料工厂处理这种区域差异就可以了。
CheesePizza:
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory=ingredientFactory;
}
@Override
void prepare() {
// TODO Auto-generated method stub
System.out.println("Preparing" + name);
dough=ingredientFactory.createDough();
sauce=ingredientFactory.createSauce();
cheese=ingredientFactory.createCheese();
}
}
*Pizza的代码利用相关的工厂生产原料。所生产的原料依赖所使用的工厂,Pizza类根本不关心这些原料,它只知道如何制作比萨。Pizza和区域原料之间被解耦。sauce=ingredientFactory.createSauce();
sauce:把Pizza的实例变量设置为此比萨所使用的某种酱料。ingredientFactory:这是原料工厂,Pizza不在乎使用什么工厂,只要是原料工厂就行了。
createSauce():此方法会返回这个区域所使用的酱料。
同样,可得到蛤蜊比萨:
public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory=ingredientFactory;
}
@Override
void prepare() {
// TODO Auto-generated method stub
System.out.println("Preparing"+name);
dough=ingredientFactory.createDough();
sauce=ingredientFactory.createSauce();
cheese=ingredientFactory.createCheese();
clam=ingredientFactory.createClam();
}
}
现在只要确认一下加盟店已经使用了正确的比萨并已经和本地的原料工厂搭上线:public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item){
Pizza pizza=null;
PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory();
if(item.equals("cheese")){
pizza=new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
}
return pizza;
}
}
完整代码:
public abstract class Dough {
}
public abstract class Sauce {
}
public abstract class Veggies {
}
public abstract class Cheese {
}
public abstract class Pepperoni {
}
public abstract class Clams {
}
public class BlackOlives extends Veggies {
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory=ingredientFactory;
}
@Override
void prepare() {
// TODO Auto-generated method stub
System.out.println("Preparing" + name);
dough=ingredientFactory.createDough();
sauce=ingredientFactory.createSauce();
cheese=ingredientFactory.createCheese();
}
}
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
// TODO Auto-generated method stub
return new ThickCrustDough();
}
@Override
public Sauce createSauce() {
// TODO Auto-generated method stub
return new PlumTomatoSauce();
}
@Override
public Cheese createCheese() {
// TODO Auto-generated method stub
return new Mozzarella();
}
@Override
public Veggies[] createVeggies() {
// TODO Auto-generated method stub
Veggies[] veggies={new BlackOlives(),new Spinach(),new EggPlant()};
return veggies;
}
@Override
public Pepperoni createPepperoni() {
// TODO Auto-generated method stub
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
// TODO Auto-generated method stub
return new FrozenClams();
}
}
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");
}
return pizza;
}
}
public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory=ingredientFactory;
}
@Override
void prepare() {
// TODO Auto-generated method stub
System.out.println("Preparing"+name);
dough=ingredientFactory.createDough();
sauce=ingredientFactory.createSauce();
cheese=ingredientFactory.createCheese();
clam=ingredientFactory.createClam();
}
}
public class EggPlant extends Veggies {
}
public class FreshClams extends Clams {
}
public class FrozenClams extends Clams {
}
public class Garlic extends Veggies {
}
public class MarinaraSauce extends Sauce {
}
public class Mozzarella extends Cheese {
}
public class Mushroom extends Veggies {
}
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
// TODO Auto-generated method stub
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
// TODO Auto-generated method stub
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
// TODO Auto-generated method stub
return new ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
// TODO Auto-generated method stub
Veggies[] veggies={new Garlic(),new Onion(),new Mushroom(),new RedPepper()};
return veggies;
}
@Override
public Pepperoni createPepperoni() {
// TODO Auto-generated method stub
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
// TODO Auto-generated method stub
return new FreshClams();
}
}
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item){
Pizza pizza=null;
PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory();
if(item.equals("cheese")){
pizza=new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
}
return pizza;
}
}
public class Onion extends Veggies {
}
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 puzza in official PizzaStore box");
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public String toString(){
//这里是打印Pizza的代码
return super.toString();
}
}
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
//每个原料都是一个类
}
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza;
pizza=createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
public class PlumTomatoSauce extends Sauce {
}
public class RedPepper extends Veggies {
}
public class ReggianoCheese extends Cheese {
}
public class SlicedPepperoni extends Pepperoni {
}
public class Spinach extends Veggies {
}
public class ThickCrustDough extends Dough {
}
public class ThinCrustDough extends Dough {
}
测试:public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore=new NYPizzaStore();
PizzaStore chicagoStore=new ChicagoPizzaStore();
Pizza pizza=nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a "+pizza.getName()+"\n");
pizza=chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a "+pizza.getName()+"\n");
}
}
结果:PreparingNew York Style Cheese Pizza
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place puzza in official PizzaStore box
Ethan ordered a New York Style Cheese Pizza
PreparingChicago Style Cheese Pizza
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place puzza in official PizzaStore box
Joel ordered a Chicago Style Cheese Pizza
*结果虽然和之前类似,但是内在实现的方式是不同的。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么,这样一来,客户就从具体的产品中被解耦。
一般类图如下:
工厂方法与抽象工厂:
联系:
抽象工厂的方法经常以工厂方法的方式实现。抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每个方法都负责创建一个具体产品,同时我们利用实现抽象工厂的子类来提供这些具体的作法。在抽象工厂中利用工厂方法实现生产方法是相当自然的作法。
区别:
工厂方法使用继承,来创建一个产品。利用工厂方法创建对象,需要扩展一个类,并覆盖它的工厂方法。抽象创建者中所实现的代码通常会用到子类所创建的具体类型。
抽象工厂使用对象的组合,来创建整个产品家族。抽象工厂提供一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。子类会创建出一组平行的产品家族。
要点:
所有的工厂都是用来封装对象的创建。
工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
抽象工厂使用对象的组合:对象的创建被实现在工厂接口所暴露出来的方法中。
所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
工厂方法允许类将实例化延迟到子类进行。
抽象工厂创建相关的对象家族,而不需要依赖他们的具体类。
自说自话:工厂方法、抽象工厂,还是有点晕的