工厂模式

工厂模式有三个:简单工厂模式,工厂模式,抽象工厂模式,继续以例子和需求的增加来引出各个模式

一.抛出需求

现在你要开一家披萨店,可以制作各种披萨,如芝士披萨(CheesePisza),意大利香肠披萨(PepperoniPizza),蛤蜊披萨(ClamPizza),素食披萨(VaggiePizza)在制作Pizza的类的函数里可能会是这样逻辑:
if(cheese)
制作芝士披萨
else if(Pepperoni)
制作意大利香肠披萨
else if(Clam)
制作蛤蜊披萨
else if(Vaggie)
制作素食披萨

缺点:
1.想新增一种希腊披萨,你需要更改这块的代码
2.素食披萨卖的不好,想不做了,又得更改这块代码
3.想在pizza中增加各种配料怎么办,又得更改代码

思路:将这里的制作pizza的过程交给一个专门的类,我们这里成为工厂,首先介绍简单工厂模式


二.简单工厂模式

因为这个逻辑比较简单,直接上代码说话
Pizza的基类,其中toppings是一些配料prepare(准备),bake(烘烤),cut(切块),box(包装)

<span style="font-size:14px;">abstract public class Pizza {
	String name;
	String dough;
	String sauce;
	ArrayList toppings = new ArrayList();

	public String getName() {
		return name;
	}

	public void prepare() {
		System.out.println("Preparing " + name);
	}

	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() {
		// code to display pizza name and ingredients
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (int i = 0; i < toppings.size(); i++) {
			display.append((String )toppings.get(i) + "\n");
		}
		return display.toString();
	}
}</span>

四种子类pizza:

<span style="font-size:14px;">public class CheesePizza extends Pizza {
	public CheesePizza() {
		name = "Cheese Pizza";
		dough = "Regular Crust";
		sauce = "Marinara Pizza Sauce";
		toppings.add("Fresh Mozzarella");
		toppings.add("Parmesan");
	}
}

public class PepperoniPizza extends Pizza {
	public PepperoniPizza() {
		name = "Pepperoni Pizza";
		dough = "Crust";
		sauce = "Marinara sauce";
		toppings.add("Sliced Pepperoni");
		toppings.add("Sliced Onion");
		toppings.add("Grated parmesan cheese");
	}
}

public class VeggiePizza extends Pizza {
	public VeggiePizza() {
		name = "Veggie Pizza";
		dough = "Crust";
		sauce = "Marinara sauce";
		toppings.add("Shredded mozzarella");
		toppings.add("Grated parmesan");
		toppings.add("Diced onion");
		toppings.add("Sliced mushrooms");
		toppings.add("Sliced red pepper");
		toppings.add("Sliced black olives");
	}
}

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");
	}
}</span>

Pizza类设计完毕,下面是PizzaStore类,在orderPizza中负责返回一个顾客订购的已经包装好的pizza,看到SimplePizzaFactory了么,别急,下面介绍,它就是制造pizza的工厂,返回一个还没prepare、bake、cut、box的pizza

<span style="font-size:14px;">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;
	}

}</span>

简单工厂类SimplePizzaFactory,就是根据顾客的订购来返回pizza

<span style="font-size:14px;">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;
	}
}</span>

测试代码:

<span style="font-size:14px;">public class PizzaTestDrive {
 
	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");
 
		pizza = store.orderPizza("veggie");
		System.out.println("We ordered a " + pizza.getName() + "\n");
	}
}</span>


可能你们要说了,这算哪门子工厂模式,有区别么,就是将本来在PizzaStore中的代码移到了SimplePizzaFactory里面而已,淡定,对呀,这就是简单工厂模式,别要求太高,抛砖引玉,慢慢来。

三.工厂模式

现在增加了一个需求,需要开两个披萨店,一个纽约风味,一个芝加哥风味,这两种风味的pizza配料完全不同,所以就算算都是CheesePizza,也都是不同的,所以上面的简单工厂模式已经不行啦,需要重新设计。先给出各个类的框图:

在这里,PizzaStore将CreatPizza委托给了pizza的各个子类自己决定。流程图如下:


上代码,首先是pizza基类

<span style="font-size:14px;">public abstract class Pizza {
	String name;
	String dough;
	String sauce;
	ArrayList toppings = new ArrayList();
 
	void prepare() {
		System.out.println("Preparing " + name);
		System.out.println("Tossing dough...");
		System.out.println("Adding sauce...");
		System.out.println("Adding toppings: ");
		for (int i = 0; i < toppings.size(); i++) {
			System.out.println("   " + toppings.get(i));
		}
	}
  
	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 in official PizzaStore box");
	}
 
	public String getName() {
		return name;
	}

	public String toString() {
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (int i = 0; i < toppings.size(); i++) {
			display.append((String )toppings.get(i) + "\n");
		}
		return display.toString();
	}
}</span>


子类NYStyleCheesePizza和ChicagoStyleCheesePizza,其它的几个类似,就不一一贴了

<span style="font-size:14px;">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 = "Chicago 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");
	}
}</span>

PizzaStore基类,在orderPizza中调用creatpizza,creatpizza在其子类中有具体实现

<span style="font-size:14px;">public abstract class PizzaStore {
 
	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;
	}
}</span>

两个store的子类NYPizzaStore和ChicagoPizzaStore,实现了createPizza,返回不同风味的pizza

<span style="font-size:14px;">public class NYPizzaStore extends PizzaStore {

	Pizza createPizza(String item) {
		if (item.equals("cheese")) {
			return new NYStyleCheesePizza();
		} else if (item.equals("veggie")) {
			return new NYStyleVeggiePizza();
		} else if (item.equals("clam")) {
			return new NYStyleClamPizza();
		} else if (item.equals("pepperoni")) {
			return new NYStylePepperoniPizza();
		} else return null;
	}
}


public class ChicagoPizzaStore extends PizzaStore {

	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;
	}
}</span>
测试函数如下:

<span style="font-size:14px;">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");

		pizza = nyStore.orderPizza("clam");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("clam");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyStore.orderPizza("pepperoni");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("pepperoni");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyStore.orderPizza("veggie");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("veggie");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");
	}
}</span>

就这个需求来说,工厂模式与简单工厂模式的区别是,前者是将pizza的制作委托给了store的子类,后者则是委托了工厂类。
工厂模式的定义:
定义了一个创建工厂的接口(PizzaStore),但由子类(NYPizzaStore)决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。
设计的指导方针:
1.变量不可以持有具体类的引用,比如直接new
2.不要让类派生自具体的类,请派生自接口或者抽象类
3.不要覆盖基类中已经实现的方法


三.抽象工厂模式-更加巧妙的工厂模式

需求再次升级,pizza总公司想控制纽约和芝加哥这两家店pizza原料,以保持向顾客提供高品质的pizza,该怎么做,工厂模式里面原料貌似都是分店自己搞定的,这样可能不是一个好的模式。一个好的模式设这样,纽约和芝加哥可能有很多pizza店,但是如果让他们自己备料,可能会产生口味不同,甚至是节约原料用劣质原料等结果,怎么办,那pizza总公司想到一个办法:在纽约和芝加哥分别建立了原料工厂,所有在纽约和芝加哥开店pizza分店,必须在各自城市的原料工程购进原料。


想建工厂也不是那么简单的,先看看上面所有的pizza都需要写什么原料
纽约风味的:
芝士pizza:番茄酱,意大利白干酪,Parmesan干酪,披萨草
素食pizza:番茄酱,意大利白干酪,Parmesan干酪,茄子,菠菜,黑橄榄
蛤蜊pizza:番茄酱,意大利白干酪,Parmesan干酪,蛤蜊
意式腊肠pizza:番茄酱,意大利白干酪,Parmesan干酪,茄子,菠菜,黑橄榄,意式腊肠
芝加哥风味:
芝士pizza:大蒜番茄酱,Riggiano干酪,大蒜
素食pizza:大蒜番茄酱,Riggiano干酪,蘑菇,洋葱,红椒
蛤蜊pizza:大蒜番茄酱,Riggiano干酪,新鲜蛤蜊
意式腊肠pizza:大蒜番茄酱,Riggiano干酪,蘑菇,洋葱,红椒,意式腊肠


首先看原料接口的代码:

<span style="font-size:18px;">public interface PizzaIngredientFactory {
 
	public Dough createDough();//面团
	public Sauce createSauce();//酱料
	public Cheese createCheese();//奶酪
	public Veggies[] createVeggies();//蔬菜
	public Pepperoni createPepperoni();//意大利腊肠
	public Clams createClam();//蛤蜊
 
}</span>
下面是纽约和芝加哥的工厂

<span style="font-size:14px;">public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
 
	public Dough createDough() {
		return new ThinCrustDough();
	}
 
	public Sauce createSauce() {
		return new MarinaraSauce();
	}
 
	public Cheese createCheese() {
		return new ReggianoCheese();
	}
 
	public Veggies[] createVeggies() {
		Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
		return veggies;
	}
 
	public Pepperoni createPepperoni() {
		return new SlicedPepperoni();
	}

	public Clams createClam() {
		return new FreshClams();
	}
}


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();
	}
}</span>

那么上面的原料工厂怎么用呢,有木有注意pizza基类一直有一个prepare方法,原料可以放在这个方法里面,下面来 改造 pizza类以及其子类
pizza基类:

<span style="font-size:14px;">	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 in official PizzaStore box");
	}

	void setName(String name) {
		this.name = name;
	}

	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();
	}
}</span>

下面这里是重点了,我们要将之前的 NYCheesePizza与ChicagoCheesePizza合并 了(其他类似),因为现在不需要这样硬性的区分, 根据原料工厂区分 即可,看代码:

<span style="font-size:14px;">public class CheesePizza extends Pizza {
	//在构造函数中初始化这个变量,就达到了区分口味的目的,不同口味赋予不同的工厂
	PizzaIngredientFactory ingredientFactory;
 
	public CheesePizza(PizzaIngredientFactory ingredientFactory) {
		this.ingredientFactory = ingredientFactory;
	}
 
	//prepare返回所有的原料
	void prepare() {
		System.out.println("Preparing " + name);
		dough = ingredientFactory.createDough();
		sauce = ingredientFactory.createSauce();
		cheese = ingredientFactory.createCheese();
	}
}</span>


因为pizza子类已经改变了,相应的 pizzaStore子类也要改变 了:

<span style="font-size:14px;">public class NYPizzaStore extends PizzaStore {
 
	protected Pizza createPizza(String item) {
		Pizza pizza = null;
		//纽约风味,则原料工厂用纽约的工厂即可,只有这样才合法有木有
		PizzaIngredientFactory ingredientFactory = 
			new NYPizzaIngredientFactory();
 
		//在构造CheesePizza时,传递的是上面已经new出来的NYPizzaIngredientFactory的实例
		if (item.equals("cheese")) {
  
			pizza = new CheesePizza(ingredientFactory);
			pizza.setName("New York Style Cheese Pizza");
  
		} else if (item.equals("veggie")) {
 
			pizza = new VeggiePizza(ingredientFactory);
			pizza.setName("New York Style Veggie Pizza");
 
		} else if (item.equals("clam")) {
 
			pizza = new ClamPizza(ingredientFactory);
			pizza.setName("New York Style Clam Pizza");
 
		} else if (item.equals("pepperoni")) {

			pizza = new PepperoniPizza(ingredientFactory);
			pizza.setName("New York Style Pepperoni Pizza");
 
		} 
		return pizza;
	}
}


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;
	}
}</span><span style="font-size:18px;">
</span>


体制已经改革了,现在jessie再想购买一个pizza,虽然在她看来订购的过程只要在店里orderPizza就可以了,实际流程已经大不一样了:




最后,抽象工厂模式指的大家仔细回味,是整个工厂模式的最高级别,设计真的很精妙有木有,那句话怎么说来着,还原了世界本来的模样




------------------------------------------------------------------------------以----上------------------------------------------------------------------------------------------------






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值