发布于 1年前

创建型设计模式:工厂方法(factory method)

情况:各子公司合作的物流公司不同

接着简单工厂与物流公司合作的例子。公司在多个地区和物流公司都有合作,但各个地区合作的物流公司有所不同,并且有些公司还是当地的物流。

简单工厂解决方案

结合简单工厂模式,我们可以创建各个地区的简单工厂。这样做先得定义简单工厂的接口:

public interface SimpleExpressFactory {
   Express createExpress(String expressType);
}

深圳,上海地区的物流简单工厂模式可以分别实现。

ShenzhenExpressFactory:

public class ShenzhenExpressFactory implements SimpleExpressFactory{
   public Express createExpress(String expressType) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
           break;
         case "YUNDA":
           express = new YunDaExpress();
           break;
          case "SHEN_ZHEN_LOCAL":
           express = new ShenzhenLocalExpress();
           break;
         default:
           express = new ShunFengExpress();
       }
       return express;
   }
}

ShanghaiExpressFactory:

public class ShanghaiExpressFactory implements SimpleExpressFactory{
   public Express createExpress(String expressType) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
           break;
         case "ZHONG_TONG":
           express = new ZhongTongaExpress();
           break;
          case "SHANG_HAI_LOCAL":
           express = new ShanghaiLocalExpress();
           break;
         default:
           express = new ShunFengExpress();
       }
       return express;
   }
}

货物发送调用的方式,在不同地区调用地区所属的物流简单工厂:

//深圳
SimpleExpressFactory factory = new ShenzhenExpressFactory();
GoodsSender sender = GoodsSender(factory);

//上海
SimpleExpressFactory factory = new ShanghaiExpressFactory();
GoodsSender sender = GoodsSender(factory);

情况:有些地区和物流公司的合作方式有所不同

在一些偏远的地方,物流公司不提供上门打包的服务,需要寄送方把货物运送到站点再打包。这种情况下,修改GoodsSender的发送流程方法,原标准发货流程:

public void send(String expressType, Goods goods) {
       Express express = expressFactory.createExpress(expressType);
       express.placeOrder(goods);
       //需要在此处为特定expressType定制合作流程
       express.pickup();
       express.delivery();
  }

问题:违反开闭原则

在上面代码的修改处,我们需要为特定地区的特定物流公司做修改,这样做合适,代码优雅吗?如果有多个特定的定制需求,那send方法就要不断修改。是否想到违反哪些开发原则呢?

是的,上述做法违反了代码的开闭原则,对修改封闭

有什么方法既能保持大部分合作物流公司的标准流程,又能够对特定流程的定制有一定的弹性空间呢?

方案:定义发送货物的标准流程框架

既然部分地区公司有定制物流公司合作流程的需求,有一种方法就是定义发货标准流程的框架的父类GoodsSender,由地区公司子类继承,这样就允许地区公司对发货流程方法send()进行修改。

因为每个地区合作的物流都要定制,那么把原来由SimpleExpressFactory的创建方法createExpress移到框架父类GoodsSender中,声明为抽象方法,由地区公司子类实现。

public abstract GoodsSender {

  public void send(String expressType, Goods goods) {
       Express express = createExpress(expressType);
       express.placeOrder(goods);
       express.pickup();
       express.delivery();
  }
  //定义创建物流的抽象方法
  abstract Express createExpress(expressType);
}

GoodsSender做了以下修改:

  1. 移除了简单工厂SimpleExpressFactory引用。
  2. 定义了用于创建物流的抽象方法createExpress,由子类实现。

修改后的GoodsSender更具弹性:

  1. 定义了物流发送的标准流程框架,用于大部分地区公司。
  2. 支持地区公司定制合作的物流公司。
  3. 允许特定发货流程的定制。

偏远地区公司的物流发送:

public AFarawayZoneGoodsSender extends GoodsSender {

  public void send(String expressType, Goods goods) {
       Express express = createExpress(expressType);
       express.placeOrder(goods);
       //修改地方
       transfGoodsToExpress(express);
       express.delivery();
  }
  //定义创建物流的抽象方法
  Express createExpress(expressType) {
       Express express;
       switch(expressType) {
          case "A_LOCAL":
           express = new ALocalExpress();
           break;
         default:
           express = new ALocalExpress();
       }
       return express;
  }
  private void transfGoodsToExpress(Express express){
      //......
  }
}

对于原Shenzhen修改如下,把原有ShenzhenSimpleExpressFactory的创建方法移到ShenzhenGoodsSender中:

public ShenzhenGoodsSender extends GoodsSender {
  Express createExpress(String expressType) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
           break;
         case "YUNDA":
           express = new YunDaExpress();
           break;
          case "SHEN_ZHEN_LOCAL":
           express = new ShenzhenLocalExpress();
           break;
         default:
           express = new ShunFengExpress();
       }
       return express;
   }
}

工厂方法模式

上面的重构后的代码有一个正式的名称:工厂方法

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

工厂方法是创建型设计模式,类图如下:

<figure class="image"></figure>### Creator:创建者接口

之所以叫工厂方法,它的一个特征是在父类定义一个用于对象创建的抽象方法,由子类实现:

abstract Product createProduct(String type)
  • abstract:定义为抽象,目的是让子类实现。
  • Product:返回的是定义产品的接口
  • createProduct:工厂方法。
  • type:参数type是可选的

ConcreteCreator:具体创建者类

覆盖工厂方法,返回不同类型的产品。请注意,工厂方法不必一直创建新实例。 它还可以从缓存、对象池或其他源返回现有对象。

Product:产品接口

定义工厂方法一个限制条件就是,具体的产品需要有共同的产品接口,这里说的不一定是interface,可以是类。

ConcreteProduct:具体产品类

具体产品是产品接口的不同实现。

工厂方法的优劣

  1. 解耦了产品的创建和具体产品绑定
  2. 符合单一职责原则,可以将产品创建代码放在一个位置,使代码更易于维护。
  3. 符合开闭原则,可以在不破坏现有代码的情况下新增产品。

简单工厂 VS 工厂方法

既然工厂方法相对简单工厂更具弹性,要不要在所有用到简单工厂的地方替换为工厂方法呢?在代码结构上,简单工厂和调用方是组合方式,工厂方法是通过继承的方式实现,代码结构相对复杂。

有一个KISS(Keep it simple)原则,如果简单工厂能满足要求,则建议使用简单工厂模式。

©2020 edoou.com   京ICP备16001874号-3