创建型设计模式:工厂方法(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;
   }
}

工厂方法模式

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

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

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

Creator:创建者接口

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

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

ConcreteCreator:具体创建者类

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

Product:产品接口

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

ConcreteProduct:具体产品类

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

工厂方法的优劣

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

简单工厂 VS 工厂方法

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

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

 

 

版权声明:著作权归作者所有。

相关推荐

检测iPhone X机型的方法

iPhone X即将上市。为了更好适配iPhone X,我们可能需要对iPhone做机型的判断。下面介绍两种方法:根据屏幕尺寸和方向判断这是iPhone机型列表。Swift 3if UIDevice().userInterfaceIdiom == .phone {      &n

[译]Android UI设计与样式——dp和sp

朋友们,最近我一直在做一些Android UI设计和样式的培训课程。 我想和更多观众分享。 这是我的第一个android ui设计和样式教程。 我将在这个主题上写更多的内容。 那么现在开始吧...DP, SP & Pixels作为Android开发人员,我们始终希望我们的UI设计与设备无关。 

Django:创建JSON响应

Django 1.7之前版本在Django1.7之前可以结合json以及HttpResponse创建json响应import json from django.http import HttpResponse response_data = {} response_data['result'] = 'er

Java创建文件的常用方法

Java创建文件有几种常用的方法File.createNewFile()创建空白文件java.io.File类里的方法createNewFile()可以用来创建文件。createNewFile()新建的是空文件。创建文件首先要使用File类构建将要被创建的文件,然后再调用createNewFile()把新文件创建出来。createNewFile()的结果分为三种情况:新文件创建成功返回true。如

Python安全创建目录的方法

在介绍Python安全创建目录之前,先举一个不安全创建目录的方式:if not os.path.exists(directory):     os.makedirs(directory) 在例子里,先判断目录是否存在,然后创建目录。这种方式是不安全的,它会导致竞争条件。在os.path.exists()和os.makedirs()之间的时

代理模式:Java基于虚拟代理(Virtual Proxy)懒加载创建耗时的对象

在java里有一些对象在创建初始化时是比较耗时,如JDBC的connection,有的时候我们希望只用到对象的简版,这种情况下我们可以使用代理的方式,在真正用到原始对象是在初始化耗时的对象。这种代理方式也叫虚拟代理(Virtual PRoxy)UML类图如下: 定义耗时类的接口public interface ExpensiveObject { void proc

Python使用os.fork()创建子进程

导入os模块首先要导入os模块,如下:import os使用os.fork()创建进程使用fork创建一个新的进程后,新进程是原进程的子进程,原进程为父进程。如果发生错误,则会抛出OSError异常。-*- coding: utf-8 -*-import timeimport ostry: pid = os.fork()except OSError: passtime.sleep(20)

模式结构

模式的结构一般分为是三个部分:需求(Forces)结果上下文(Resulting Context)相关模式(Related Patterns)说明如下:需求是必须解决的问题,它描述了必须解决的问题和围绕这个特定问题的上下文环境。需求有时候会发生冲突,必须予以取舍,选择最重要的需求来解决。结果上下文是采用模式后可能带来的后果,它描述了采用这个模式后的结果,它包含三个部分: 好处,弊端和问题。相关模式

RxJs——创建型操作

我们在使用RxJs中,知道RxJs的操作分为两类,一类是创建型,比如of(),fromEvent(),from()等,还有一类是操作型,比如map(),filter()。而这两类操作的基础和心是一个叫做Observable的对象,如果对Observable不甚理解,需要快速理解的可以查看这一篇今天我们来学习第一类,尝试着自己实现一次。of()这个操作接收数组参数,返回一个Observable,一旦

CKEditor5——模型理解(一)

我们知道,CK5实现了一个MVC的架构,从今天开始,我们一步一步深入学习模型,视图,以及模型和视图之间的转换。今天我们开始模型的学习。首先,我们看模型的定义:The model is implemented by a DOM-like tree structure of elements and text nodes.模型由两类节点构成,分别是元素节点和文本节点,模型是一种类Dom树结构。我们知道