zoukankan      html  css  js  c++  java
  • 设计模式——工厂模式

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

    工厂方法模式定义:定义了一个创建对象的接口(这里的接口并不是单单是指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 type
    12   * @return
    13   */
    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 type
    29   * @return
    30   */
    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  @Override
    11  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  @Override
    10  public Dough orderDough() {
    11   Dough dough = new Dough();
    12   dough.setName("黑龙江优质小麦");
    13   return dough;
    14  }
    15 
    16  @Override
    17  public Shallot orderShallot() {
    18   Shallot shallot = new Shallot();
    19   shallot.setName("山东大葱");
    20   return shallot;
    21  }
    22 
    23  @Override
    24  public Chili orderChili() {
    25   Chili chili = new Chili();
    26   chili.setName("西北红辣椒");
    27   return chili;
    28  }
    29 
    30  @Override
    31  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  @Override
    18  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 }
    对于抽象工厂模式的总结:抽象工厂也是工厂模式的一种,本质上还是封装对象的创建。在本例子中我们可以看到,抽象工厂将食材的创建全部封装到抽象工厂中,不同的地区可以根据当地的特点来创建属于本地区的食材供应市场,而整个程序不需要因为地域之间不同而发生改变,每个区域只要创建一个食材供应市场,然后在自己店铺的系统中指定它既可。

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

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

  • 相关阅读:
    党报
    一个人只有敢于承担责任,才有可能被赋予更大的责任。做不
    勇于担当:好男人的三块责任田——
    关于担当
    领导干部要勇于担当
    福布斯专访阿里蔡崇信:马云的坚持和改变
    阿里股权
    ContentProvider
    搞笑段子
    报业
  • 原文地址:https://www.cnblogs.com/xiaochao-cs-whu/p/3918337.html
Copyright © 2011-2022 走看看