设计模式第五天之工厂模式
在这一次分享中,向大家介绍一下工厂模式。工厂模式简单的说就是将对象创建的细节封装起来,实现这样目的的方式有三种:简单工厂、工厂方法模式和抽象工厂模式。在介绍着几个方式之前,首先来向大家介绍一个设计原则—依赖倒置原则。
什么是依赖倒置原则呢?官方说:“要依赖抽象,不要依赖具体类”,这和针对接口编程很相似。通过一个例子让大家了解依赖倒置原则。
假设我要去披萨店点披萨,一共有3种披萨CheesePizza,VeggiePizza,ClamPizza。通过PizzaStore的orderPizza()方法来点披萨。代码是这样的。
1 import pizza.CheesePizza; 2 import pizza.ClamPizza; 3 import pizza.Pizza; 4 import pizza.VeggiePizza; 5 /** 6 * 披萨店 7 * @author wangpeiyu 8 * 9 */ 10 public class PizzaStore { 11 public Pizza orderPizza(String type){ 12 Pizza pizza=null; 13 if(type.equals("cheese")){ 14 pizza=new CheesePizza(); 15 }else if(type.equals("veggie")) 16 { 17 pizza = new VeggiePizza(); 18 }else if(type.equals("clam")) 19 { 20 pizza = new ClamPizza(); 21 } 22 return pizza; 23 } 24 25 }
在UML中,将上述类间关系画出来是这样的。

可以看到PizzaStore类依赖Pizza及所有他的子类,这样导致依赖非常严重,高耦合。对于在上面的代码,你会发现,如果你要新增一种披萨,除了要新建一个Pizza子类外,还要在PizzaStore的orderPizza()方法中修改相应的代码,这样导致耦合度很高。
如果我们把orderPizza()方法中创建Pizza对象的代码块抽取出来,放到另外的一个对象中专门处理,如下面的代码。
PizzaStore类
1 import factory.PizzaFactory; 2 import pizza.CheesePizza; 3 import pizza.ClamPizza; 4 import pizza.Pizza; 5 import pizza.VeggiePizza; 6 /** 7 * 披萨店 8 * @author wangpeiyu 9 * 10 */ 11 public class PizzaStore { 12 public Pizza orderPizza(String type){ 13 Pizza pizza=null; 14 /* if(type.equals("cheese")){ 15 pizza=new CheesePizza(); 16 }else if(type.equals("veggie")) 17 { 18 pizza = new VeggiePizza(); 19 }else if(type.equals("clam")) 20 { 21 pizza = new ClamPizza(); 22 }*/ 23 pizza = PizzaFactory.createPizza(type); 24 /** 25 * 再执行其他的相关操作 26 */ 27 return pizza; 28 } 29 30 }
PizzaFactory工厂类
1 package factory; 2 import pizza.CheesePizza; 3 import pizza.ClamPizza; 4 import pizza.Pizza; 5 import pizza.VeggiePizza; 6 /** 7 * 披萨工厂类 8 * 专职处理披萨对象的创建 9 * 如果要创建一个披萨对象,直接找这个对象就可以了 10 * @author wangpeiyu 11 * 12 */ 13 public class PizzaFactory { 14 /** 15 * 生成具体的披萨对象 16 * @param type 披萨的类型 17 * @return 返回生成的指定披萨实例 18 */ 19 public static Pizza createPizza(String type) 20 { 21 Pizza pizza=null; 22 if(type.equals("cheese")){ 23 pizza=new CheesePizza(); 24 }else if(type.equals("veggie")) 25 { 26 pizza = new VeggiePizza(); 27 }else if(type.equals("clam")) 28 { 29 pizza = new ClamPizza(); 30 } 31 return pizza; 32 } 33 }
这时候PizzaStore的UML图是这样的。

你会发现依赖图原本由上而下的,现在倒置了。换句话说:不能让高层组件(PizzaStore,Pizza)依赖低层组件(Pizza子类),而且不管是高层还是低层组件,两者都应该依赖抽象。
上面将创建对象的代码块抽取出来,放到PizzaFactory中去实现的方式,我们成为简单工厂。这时候,如果你要增加一种披萨,PizzaStore中的所有代码都不需要更改,你只有修改专门负责创建pizza对象的PizzaFactory类中的createPizza方法就可以了。而且在其他任何地方用到创建Pizza对象也直接用PizzaFactory就可以了,这样即使需要修改也是修改一处,提高了代码的重用,降低了耦合。
下面来介绍一下另一种方式,工厂方法模式。
改变一下需求,上面说的三种披萨各有二种风味的,分别为纽约风味和伦敦风味。因此及现在Pizza的子类就有6个了。分别为
NYCheesePizza,LDCheesePizza,NYVeggiePizza,LDVeggiePizza,NYClamPizza,LDClamPizza.
传统我们是怎么实现的呢。
在PizzaStore类中的orderPizza()方法是代码这样的。
1 public Pizza orderPizza(String style,String type) 2 { 3 Pizza pizza = null; 4 if(style.equals("NewYork")){ 5 if(type.equals("cheese")){ 6 pizza=new NYCheesePizza(); 7 }else if(type.equals("veggie")) 8 { 9 pizza = new NYVeggiePizza(); 10 }else if(type.equals("clam")) 11 { 12 pizza = new NYClamPizza(); 13 } 14 }else if(style.equals("London")){ 15 if(type.equals("cheese")){ 16 pizza=new LDCheesePizza(); 17 }else if(type.equals("veggie")) 18 { 19 pizza = new LDVeggiePizza(); 20 }else if(type.equals("clam")) 21 { 22 pizza = new LDClamPizza(); 23 } 24 } 25 return pizza; 26 }
你会发现,判断特别的多。如果还要增加新的pizza或者风味,那这么判断就不可想象了,非常容易错误。经过思考,我们可以将PizzaStore作为基类,创建一个抽象的方法createPizza(),不同的风味的披萨的创建作为一个子类,那这个子类就必须实现createPizza()方法,这个方法只创建属于这个子类风味的披萨。这样要创建不同风味的披萨,则创建相应的子类对象赋值给PizzaStore基类引用,利用多态,PizzaStore引用可以指向不同的风味,则可以创建不同的风味披萨了,而且即使要增加新的风味,则继承PizzaStore实现该特定风味的披萨的createPizza()方法就可以了。
下面是代码的实现。
基类PizzaStore类的代码,PizzaStore类为抽象类,增加了一个抽象的方法createPizza(),子类必须要实现这个方法。
1 import factory.PizzaFactory; 2 import pizza.CheesePizza; 3 import pizza.ClamPizza; 4 import pizza.LDCheesePizza; 5 import pizza.LDClamPizza; 6 import pizza.LDVeggiePizza; 7 import pizza.NYCheesePizza; 8 import pizza.NYClamPizza; 9 import pizza.NYVeggiePizza; 10 import pizza.Pizza; 11 import pizza.VeggiePizza; 12 /** 13 * 披萨店 14 * @author wangpeiyu 15 * 16 */ 17 public abstract class PizzaStore { 18 public Pizza orderPizza(String type){ 19 Pizza pizza=null; 20 /* if(type.equals("cheese")){ 21 pizza=new CheesePizza(); 22 }else if(type.equals("veggie")) 23 { 24 pizza = new VeggiePizza(); 25 }else if(type.equals("clam")) 26 { 27 pizza = new ClamPizza(); 28 }*/ 29 //这个是简单工厂实现 30 //pizza = PizzaFactory.createPizza(type); 31 //下面是工厂方法模式实现的 32 pizza = createPizza(type); 33 /** 34 * 再执行其他的相关操作 35 */ 36 return pizza; 37 } 38 39 public abstract Pizza createPizza(String type); 40 /** 41 * 传统的实现不同风味的披萨创建 42 * @param style 风味 43 * @param type 披萨的类型 44 * @return 返回指定风味的指定披萨 45 */ 46 /*public Pizza orderPizza(String style,String type) 47 { 48 Pizza pizza = null; 49 if(style.equals("NewYork")){ 50 if(type.equals("cheese")){ 51 pizza=new NYCheesePizza(); 52 }else if(type.equals("veggie")) 53 { 54 pizza = new NYVeggiePizza(); 55 }else if(type.equals("clam")) 56 { 57 pizza = new NYClamPizza(); 58 } 59 }else if(style.equals("London")){ 60 if(type.equals("cheese")){ 61 pizza=new LDCheesePizza(); 62 }else if(type.equals("veggie")) 63 { 64 pizza = new LDVeggiePizza(); 65 }else if(type.equals("clam")) 66 { 67 pizza = new LDClamPizza(); 68 } 69 } 70 return pizza; 71 }*/ 72 }
纽约风味的披萨的NYPizzaStore类代码,继承与PizzaStore类,必须实现PizzaStore类中的抽象方法createPizza().
1 import pizza.NYCheesePizza; 2 import pizza.NYClamPizza; 3 import pizza.NYVeggiePizza; 4 import pizza.Pizza; 5 /** 6 * 纽约风味的披萨店 7 * 专门创建纽约风味的pisa 8 * @author wangpeiyu 9 * 10 */ 11 public class NYPizzaStore extends PizzaStore { 12 13 @Override 14 public Pizza createPizza(String type) { 15 Pizza pizza = null; 16 if(type.equals("cheese")){ 17 pizza=new NYCheesePizza(); 18 }else if(type.equals("veggie")) 19 { 20 pizza = new NYVeggiePizza(); 21 }else if(type.equals("clam")) 22 { 23 pizza = new NYClamPizza(); 24 } 25 return pizza; 26 } 27 28 }
下面是伦敦风味的披萨类,与纽约的一致。
1 import pizza.LDCheesePizza; 2 import pizza.LDClamPizza; 3 import pizza.LDVeggiePizza; 4 import pizza.Pizza; 5 /** 6 * 伦敦风味的披萨 7 * 专门创建负责伦敦风味的披萨 8 * @author wangpeiyu 9 * 10 */ 11 public class LDPizzaStore extends PizzaStore { 12 @Override 13 public Pizza createPizza(String type) { 14 Pizza pizza=null; 15 if(type.equals("cheese")){ 16 pizza=new LDCheesePizza(); 17 }else if(type.equals("veggie")) 18 { 19 pizza = new LDVeggiePizza(); 20 }else if(type.equals("clam")) 21 { 22 pizza = new LDClamPizza(); 23 } 24 return pizza; 25 } 26 }
下面来创建一个测试类,来测试工厂方法模式。
1 package test; 2 3 import pizza.LDClamPizza; 4 import store.LDPizzaStore; 5 import store.NYPizzaStore; 6 import store.PizzaStore; 7 8 public class main { 9 10 public static void main(String[] args) { 11 //新建一个PizzaStore基类引用 12 PizzaStore store=null; 13 /** 14 * 小明点了一个纽约风味的cheese披萨 15 */ 16 //赋值纽约风味的对象 17 store = new NYPizzaStore(); 18 store.orderPizza("cheese").tostring(); 19 /** 20 * 小红点了一个;伦敦风味的clam披萨 21 */ 22 //赋值纽约风味的对象 23 store = new LDPizzaStore(); 24 store.orderPizza("clam").tostring(); 25 } 26 27 }
输出的结果为:
NYCheesePizza
LDClamPizza
可以看出已经订了相应风味的执行披萨。
现在我想在生产披萨对象的时候指定原料的产地,我们假设原料产地有两个地方,纽约原料产地和伦敦原料产地,NYSourceFactory和LDSourceFactory类。为了满足针对接口编程的原则,我们要建立一个原料产地的基类。SourceFactory,在创建披萨的时候要将原料产地对象传递进去。现在将代码修改后为:
SourceFactory类
1 package factory; 2 3 /** 4 * 抽象工厂模式 5 * 原料工厂 6 * @author wangpeiyu 7 * 8 */ 9 public interface SourceFactory { 10 public String createDough(); 11 public String createSauce(); 12 }
下面是相应的原料工厂
纽约原料工厂:
package factory; /** * 纽约的原料产地 * @author wangpeiyu * */ public class NYSourceFactory implements SourceFactory { @Override public String createDough() { // TODO Auto-generated method stub return "ny dough"; } @Override public String createSauce() { // TODO Auto-generated method stub return "ny sauce"; } }
伦敦原料工厂
1 package factory; 2 /** 3 * 伦敦的原料产地 4 * @author wangpeiyu 5 * 6 */ 7 public class LDSourceFactory implements SourceFactory { 8 9 @Override 10 public String createDough() { 11 // TODO Auto-generated method stub 12 return "ld dough"; 13 } 14 15 @Override 16 public String createSauce() { 17 // TODO Auto-generated method stub 18 return "ld sauce"; 19 } 20 21 }
因为创建pizza要指定原料,所以pizza类中的材料要进行初始化,初始化为相应的原料。在不同的披萨中,添加的原料可能不同。所以在pizza中创建一个抽象的方法prepare,用来准备材料,在子类中必须实现这个方法,初始化相应的材料。
Pizza类
1 package pizza; 2 3 public abstract class Pizza { 4 /** 5 * 名称 6 */ 7 protected String name; 8 /** 9 * 面团类型 10 */ 11 protected String dough; 12 /** 13 * 酱料类型,这是一种佐料 14 */ 15 protected String sauce; 16 17 public void tostring() 18 { 19 System.out.println("Pizza"); 20 } 21 public abstract void prepare(); 22 }
下面是NYCheesePizza披萨
1 package pizza; 2 3 import factory.SourceFactory; 4 5 public class NYCheesePizza extends Pizza { 6 SourceFactory factory; 7 public NYCheesePizza(SourceFactory factory) { 8 this.factory = factory; 9 } 10 public void tostring() 11 { 12 System.out.println("NYCheesePizza" + dough+" "+sauce); 13 } 14 15 @Override 16 public void prepare() { 17 dough = factory.createDough(); 18 sauce = factory.createSauce(); 19 } 20 }
其他的五种披萨同理。
NYPizzaStore披萨店类
1 package store; 2 import factory.NYSourceFactory; 3 import pizza.NYCheesePizza; 4 import pizza.NYClamPizza; 5 import pizza.NYVeggiePizza; 6 import pizza.Pizza; 7 /** 8 * 纽约风味的披萨店 9 * 专门创建纽约风味的pisa 10 * @author wangpeiyu 11 * 12 */ 13 public class NYPizzaStore extends PizzaStore { 14 NYSourceFactory factory=new NYSourceFactory(); 15 @Override 16 public Pizza createPizza(String type) { 17 Pizza pizza = null; 18 if(type.equals("cheese")){ 19 pizza=new NYCheesePizza(factory); 20 }else if(type.equals("veggie")) 21 { 22 pizza = new NYVeggiePizza(factory); 23 }else if(type.equals("clam")) 24 { 25 pizza = new NYClamPizza(factory); 26 } 27 return pizza; 28 } 29 30 }
LDPizzaStore类
1 package store; 2 import factory.LDSourceFactory; 3 import factory.NYSourceFactory; 4 import pizza.LDCheesePizza; 5 import pizza.LDClamPizza; 6 import pizza.LDVeggiePizza; 7 import pizza.Pizza; 8 /** 9 * 伦敦风味的披萨 10 * 专门创建负责伦敦风味的披萨 11 * @author wangpeiyu 12 * 13 */ 14 public class LDPizzaStore extends PizzaStore { 15 LDSourceFactory factory=new LDSourceFactory(); 16 @Override 17 public Pizza createPizza(String type) { 18 Pizza pizza=null; 19 if(type.equals("cheese")){ 20 pizza=new LDCheesePizza(factory); 21 }else if(type.equals("veggie")) 22 { 23 pizza = new LDVeggiePizza(factory); 24 }else if(type.equals("clam")) 25 { 26 pizza = new LDClamPizza(factory); 27 } 28 return pizza; 29 } 30 }
在这里规定了NYPizzaStore商店用的是NYSourceFactory工厂的原料。但是真正的抽象工厂模式不是在这里,而是在各个Pizza的子类中,通过SourceFactory的引用factory去动态的创建原料,这里是创建了两种材料,createDough和createSauce。
测试类:
1 package test; 2 3 import pizza.LDClamPizza; 4 import pizza.Pizza; 5 import store.LDPizzaStore; 6 import store.NYPizzaStore; 7 import store.PizzaStore; 8 9 public class main { 10 11 public static void main(String[] args) { 12 //新建一个PizzaStore基类引用 13 PizzaStore store=null; 14 Pizza pizza=null; 15 /** 16 * 小明点了一个纽约风味的cheese披萨 17 */ 18 //赋值纽约风味的对象 19 store = new NYPizzaStore(); 20 pizza = store.orderPizza("cheese"); 21 pizza.prepare(); 22 pizza.tostring(); 23 /** 24 * 小红点了一个;伦敦风味的clam披萨 25 */ 26 //赋值纽约风味的对象 27 store = new LDPizzaStore(); 28 pizza = store.orderPizza("clam"); 29 pizza.prepare(); 30 pizza.toString(); 31 } 32 33 }
测试的结果是:
NYCheesePizza ny dough ny sauce
LDCheesePizza ld dough ld sauce
已经实现了动态的生成原料。
工厂方式模式和抽象工厂模式其实相似,但是也有不同。不同在于工厂方法模式是基于基类派生出来的,而且只能创建一个对象。而抽象工厂模式则是生产一系列的产品族,在这个例子中是生产了dough和sauce原料,而且是在其他的对象中,通过组合来实现的。
总而言之,工厂模式就是将对象的创建细节封装起来。工厂方法模式是将对象的创建置于子类中实现,让子类决定创建哪些实例。抽象工厂模式则是将对象的创建置于工厂对象中,可以利用多态创建相应的产品族。

浙公网安备 33010602011771号