首页 > 代码库 > 设计模式——工厂模式

设计模式——工厂模式

工厂模式有两种类型:工厂方法模式和抽象工厂模式

工厂方法模式定义:定义了一个创建对象的接口(这里的接口并不是单单是指java中的interface),但由子类决定实例化的类是哪一个。工厂方法让类把实例化推迟到子类中。
抽象工厂模式定义:提供一个接口(这里的接口并不是单单是指java中的interface),用于创建相关或依赖对象的家族,而不需要明确指定具体的类。

    不管工厂模式的形式是哪一种,我们要清楚的知道工厂模式的作用,工厂模式是用来封装对象的创建的!也就是说工厂模式用来管理创建对象的,其他的一概和工厂模式无关。
    通过封装对象的创建,就可以达到松耦合的目的,因为滥用new关键字会将多个对象绑定起来,从而耦合在一起,而工厂模式就是要将这些耦合解开,然后以最小的耦合来达到相同的目的。
    比如说:现在要在多个村庄之间组建电网,第一种方案就是“图方便原则”,我们忽略掉村与村之间的界限,只要两个住户之间相隔近,不管他俩是不是一个村,我们都将这两家住户连在同一根电线上,当扩展到成百上千的住户的时候,我们可以想象,这时的电路网会错综复杂,难以管理,当需要更改电网的时候,也是牵一发而动全身。第二个方案是将住户以村为单位,或者更小的,我们以生产队为单位,一个生产队里面的住户采取“图方便原则”,而生产队之间用一根电线连接起来,这样生产队内部的调整不会影响到其他的生产队,也便于管理,这样就以最小的耦合来达到了相同的目的。实际上这里的每一个电线连接处我们都可以将其视为一个new关键字。
 
当然上面的例子只是宽泛的类比了一下工厂模式,例子不一定准确,下面通过一个真正的实例来介绍工厂模式中的工厂方法模式
  • 需求说明
    假设现在有一家面食店,在全国都有连锁店,我们要开发一个系统,让计算机来控制机器帮我们做面食。面点的做法在步骤上基本上都差不多:准备材料→烧水下面(同时也加入了调料,我们的调料都是实现和面拼好的)→装盘。但是全国各地的面食的差别主要在于食材的选择,调料的搭配方面。我们的店要入乡随俗,生产符合当地口味的面食,所以我们的系统要依据地方而有不同的实现。我们不能针对每一个地方都单独的开发一套系统,这样会导致成本过高,我们需要做的就是将公共的地方提出来,然后根据不同地域来实现那些没有公共部分的代码。
  • 系统设计
    java只给我们提供了一个new关键字来创建对象,设计模式不是要求我们不使用这个关键字(不允许使用的话,连对象都创建不了了),而是要求我们不要到处滥用这个关键字,最好是将这个关键字的使用限定在某些区域,这样就便于更改和管理。
    需要使用new的地方,就是对象之间关联的地方,也就是耦合发生的地方,虽然完全消除对象之间的关联是一件不可能的事情,但是我们可以在保证需求的前提下,尽量的减少耦合,或者将耦合全部集中到一个易于管理的地方。
    对于 本例子来说,对象之间的关联在于:①具体的店铺和抽象店铺的关联;②具体面食与抽象面食之间的关联。我们可以将抽象店铺与具体店铺之间用一个new关键字来关联,同时将同一类店铺中所有的面食种类全部封装在店铺内部,这样每个店铺就可以管理自己的面食,店铺与店铺之间不相关联。
 
  • 系统实现
 1 面食抽象类: 2 /** 3  * 声明为抽象方法,不能直接new,实例化交给工厂来做 4  * @author Apache_xiaochao 5  * 6  */ 7 public abstract class Noodle { 8  9  private String name; // 名称10  private String dough; // 面团11  private List<String> spices; // 调料12 13  /**14   * 准备食材15   */16  public void prepare() {17   System.out.println(18     "正在准备食材,面食名称:" + this.getName() +19     " 面团名称:" + this.getDough() +20     " 调料:" + spices.toString());21  }22 23  /**24   * 烧水25   */26  public void boilWater() {27       System.out.println("正在烧水...");28  }29 30  /**31   * 煮面32   */33  public void cook() {34       System.out.println("正在煮面...");35  }36 37  /**38   * 装盘39   */40  public void sabot() {41       System.out.println("正在装盘...");42  }    43     //余下的get和set方法,省略...44 }
 1 面食店抽象类: 2 /**  3  * 面食店抽象类,所有的面食店都需要继承这个抽象类 4  * @author Apache_xiaochao 5  * 6  */ 7 public abstract class NoodleStore { 8   9 /**10   * 根据用户需求制作相应的面食11   * @param type12   * @return13   */14  public Noodle orderNoodle(String type){15   Noodle noodle = null;16   if(type != null){17    noodle = createNoodle(type);18    noodle.prepare();19    noodle.boilWater();20    noodle.cook();21    noodle.sabot();22   }23   return noodle;24  }25  26  /**27   * 工厂方法 ,将new全部集中到这个方法里面去,然后将new延迟到子类中实现28   * @param type29   * @return30   */31  protected abstract Noodle createNoodle(String type);       //这里是核心,工厂方法就在这32 //上面定义了一个抽象的工厂方法,也可以不是抽象的,我们可以在这里定义一个默认的工厂。33 //这里讲所有的new全部集中到工厂方法中进行管理,每个子类都可以在该方法中管理自己的对象34 //这里采用的是“参数化工厂方法”,可以根据传递的参数而创建不同的对象,然而工厂常常只产生一种对象,因此不需要参数化35 36 }
 1 具体的面食店,以山西面食馆为例: 2 /** 3  * 山西面食馆 4  * 5  * @author Apache_xiaochao 6  * 7  */ 8 public class ShanXiNoodleStore extends NoodleStore { 9 10  @Override11  protected Noodle createNoodle(String type) {12   Noodle noodle = null;13   if (type != null) {14    if ("saozi".equalsIgnoreCase(type)) {15     noodle = new SXSaoziNoodle();16    } else if ("liujian".equalsIgnoreCase(type)) {17     noodle = new SXLiujianNoodle();18    }19   }20   return noodle;21  }22 23 }24     在这个具体的面食店里面,我们使用了很多的new,这些new可以轻松被管理,如果以后这家店希望添加新的面食,或者减少不受欢迎的面食,改起来就方便很多。
整个架构中代码执行过程说明:
  • 我们先在某个地方开一家山西面食馆:NoodleStore noodleStore = new ShanXiNoodleStore();  //店铺开起来了
  • 假设现在有一位顾客走进我们新开的面食馆,点了一份臊子面:noodleStore.orderNoodle("saozi");  //来生意啦
  • 点完餐之后,系统开始运行,步骤如下:

因为是在山西面食馆里面点餐,所以是山西面食馆里面的系统在为我们服务,当指定面食类型之后,系统就会为我们准备后相应的食材,然后开始制作面食(这些基本上都是大同小异,如果有不同的地方,可以覆盖父类中的相关方法)
 
对于工厂方法的总结:
    工厂模式都是用来封装对象的创建的。工厂方法模式属于工厂模式,当然也是为封装对象创建而服务的,由上面的例子我们可以看,所有对象的创建都被封装到工厂方法之中,而且这个方法的实现,并没有在NoodleStore这个类中,而是在NoodleStore的子类中,因为子类更加清楚要做什么,这样就在一定程度上达到了松耦合的目的,并且让代码更加清晰、灵活、易于扩展。
再次给出工厂方法模式的定义:定义一个创建对象的接口(这里的接口就是指工厂方法),但由子类决定要实例化的类是哪一个(工厂方法的实现在子类中)。工厂方法让类把实例化推迟到子类。

接下来我们仍然使用这个例子来讲解抽象工厂模式
  • 需求说明
    为了保质保量,我们需要控制所有连锁店的食材来源的质量,所以我们决定每个省都指定一个专门的工厂用于提供食材。
  • 系统设计
    这一次我们选用抽象工厂,既然也属于工厂模式,那么它的作用还是用来封装对象的创建。(下图为相应UML图,不过已经没有什么可看性了,太复杂)
  • 系统实现
 1 抽象工厂接口: 2 /**  3  * 抽象工厂 4  * @author Apache_xiaochao 5  * 6  */ 7 public interface NoodleIngredientFactory { 8   9  public abstract Dough orderDough();     //提供面团10  public abstract Shallot orderShallot();     //提供小葱11  public abstract Chili orderChili();     //提供辣椒12  public abstract Egg orderEgg();     //提供鸡蛋13  14 }15     抽象工厂中定义了各种各样食材的供应方法,这些都返回了相应的食材对象,肯定需要用到很多new关键字,这里我们用抽象工厂将它们封装起来。
 1 具体工厂的实现(以东北食材供应工厂为例): 2 /**  3  * 东北原料工厂,这个工厂为东北全境的东北面食馆提供食材 4  * @author Apache_xiaochao 5  * 6  */ 7 public class DongBeiIngredientFactory implements NoodleIngredientFactory { 8  9  @Override10  public Dough orderDough() {11   Dough dough = new Dough();12   dough.setName("黑龙江优质小麦");13   return dough;14  }15 16  @Override17  public Shallot orderShallot() {18   Shallot shallot = new Shallot();19   shallot.setName("山东大葱");20   return shallot;21  }22 23  @Override24  public Chili orderChili() {25   Chili chili = new Chili();26   chili.setName("西北红辣椒");27   return chili;28  }29 30  @Override31  public Egg orderEgg() {32   Egg egg = new Egg();33   egg.setName("农家土鸡蛋");34   return egg;35  }36 37 }
 1 具体面食馆的实现(以东北面食馆为例): 2 /** 3  * 东北面食馆 4  * 5  * @author Apache_xiaochao 6  * 7  */ 8 public class DongBeiNoodleStore extends NoodleStore { 9  10  private NoodleIngredientFactory nif;11  12  //在构造方法中指定由哪个食材工厂来提供食材13  public DongBeiNoodleStore() {14       this.nif = new DongBeiIngredientFactory();15  }16 17  @Override18  protected Noodle createNoodle(String type) {19   Noodle noodle = null;20   if (type != null) {21    if ("yangchun".endsWith(type)) {22     noodle = new DBYangchunNoodle(nif);23    } else if ("dawan".equalsIgnoreCase(type)) {24     noodle = new DBDawanNoodle(nif);25    }26   }27   return noodle;28  }29 30 }
对于抽象工厂模式的总结:抽象工厂也是工厂模式的一种,本质上还是封装对象的创建。在本例子中我们可以看到,抽象工厂将食材的创建全部封装到抽象工厂中,不同的地区可以根据当地的特点来创建属于本地区的食材供应市场,而整个程序不需要因为地域之间不同而发生改变,每个区域只要创建一个食材供应市场,然后在自己店铺的系统中指定它既可。

工厂方法与抽象工厂之间的区别:
  • 利用工厂方法创建对象,需要扩展一个类,并覆盖对应的工厂方法
  • 提供一个封装对象创建的抽象类型,这个类型的子类或者是实现类定义了对象创建的方法,然后实例化这个工厂,并将其传到需要需要使用它的地方
  • 工厂方法封装的是某一类对象的创建,每次只提供一个对象
  • 抽象工厂封装的是一群相关的产品集合,可以同时提供多个对象
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中

如有错误,恳请读者指正!