设计模式第五天之工厂模式

在这一次分享中,向大家介绍一下工厂模式。工厂模式简单的说就是将对象创建的细节封装起来,实现这样目的的方式有三种:简单工厂、工厂方法模式和抽象工厂模式。在介绍着几个方式之前,首先来向大家介绍一个设计原则—依赖倒置原则。

什么是依赖倒置原则呢?官方说:“要依赖抽象,不要依赖具体类”,这和针对接口编程很相似。通过一个例子让大家了解依赖倒置原则。

假设我要去披萨店点披萨,一共有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原料,而且是在其他的对象中,通过组合来实现的。

总而言之,工厂模式就是将对象的创建细节封装起来。工厂方法模式是将对象的创建置于子类中实现,让子类决定创建哪些实例。抽象工厂模式则是将对象的创建置于工厂对象中,可以利用多态创建相应的产品族。

posted @ 2016-11-19 22:10  于王令  阅读(89)  评论(0)    收藏  举报