发布于 1年前

创建型设计模式:简单工厂(simple factory)

假设你在开发一个商品发货系统,公司没有物流运输能力的,需要对接快递公司。刚开始,货物是交给一个快递公司,你的发货流程简单描述为下单,收货,货物运送。代码类似于:

public void send(Goods goods) {
       Expess express = new Express();
       express.placeOrder(goods);
       express.pickup();
       express.delivery();
}

此时快递Express对象是直接通过new构建。

合作的快递公司增多

后来公司增加了顺丰,邮政作为货物的快递公司,因为给快递公司的发货流程不变,还是下单,收货和货物运送。所以抽象了Express,新增ShunFengExpress和ChinaPostExpress继承于Express。发货代码就改为:

public void send(String expressType, Goods goods) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
         case "CHINA_POST":
           express = new ChinaPostExpress();
         default:
           express = new ShunFengExpress();
       }
       express.placeOrder(goods);
       express.pickup();
       express.delivery();
}

send()方法增加了expressType参数,用来决定使用哪家快递公司来发货。

问题:合作的快递公司名单不时变更

随着业务的开展,公司合作的快递公司有时要发生变更,如增加韵达,去掉邮政等。如果要满足这个需求的话,我们就需要不断修改send()方法里的代码:

public void send(String expressType, Goods goods) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
         case "YUNDA":
           express = new YunDaExpress();
         case "ZHONG_TONG":
           express = new ZhongTongaExpress();
         default:
           express = new ShunFengExpress();
       }
       express.placeOrder(goods);
       express.pickup();
       express.delivery();
}

对上面的代码review,你会发现发货流程(下单,收货和送货)本身是没有任何变化的,send()方法中,唯一发生变化的是创建快递公司的部分,而且是随着业务变化要不时修改。

这违背了“开闭原则”!开闭原则要求我们对扩展开发,对修改关闭。

简单工厂:封装会发生变化的创建对象代码

现在你知道会变化的部分是创建Express对象的代码。把创建代码抽离出来,封装在一个新的类中,这个类我们称为简单工厂。SimpleExpressFactory就负责Express对象的创建,代码类似于:

public class SimpleExpressFactory {
   public Express createExpress(String expressType) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
         case "YUNDA":
           express = new YunDaExpress();
         case "ZHONG_TONG":
           express = new ZhongTongaExpress();
         default:
           express = new ShunFengExpress();
       }
       return express;
   }
}

SimpleExpressFactory的createExpress方法接受expressType作为创建Express对象的参数。新的发货系统修改为:

public GoodsSender {
  SimpleExpressFactory expressFactory;

  public GoodsSender(SimpleExpressFactory expressFactory) {
    this.expressFactory = expressFactory;
  }
  public void send(String expressType, Goods goods) {
       Express express = expressFactory.createExpress(expressType);
       express.placeOrder(goods);
       express.pickup();
       express.delivery();
  }
}

疑问

你会发现创建代码只是从一个类迁移到了另一个类,如果业务变化,还是要修改SimpleExpressFactory里的代码。不时修改的问题仍然存在。那这样做有什么优势呢?

抽离封装创建代码的好处:

  1. 将对象的创建与对象的使用过程分离,调用者只需要调用创建方法获取对象即可,不需要关心创建对象过程
  2. 如果有多个地方需要获取对象,简单工厂就可以被复用。如采购系统要查下各个快递的价格等。
  3. 把通过new创建具体实例的代码移除,这样GoodsSender就不依赖于Express的具体实例。

简单工厂的变种:静态工厂方法

简单工厂方法往往只需要一个创建的方法,一个常见的技巧是,把简单工厂定义为静态方法,这种做法称为静态工厂方法。代码就类似于:

public GoodsSender {

  public void send(String expressType, Goods goods) {
       Express express = GoodsSender.createExpress(expressType);
       express.placeOrder(goods);
       express.pickup();
       express.delivery();
  }

  public static Express createExpress(String expressType) {
       Express express;
       switch(expressType) {
         case "SHUNFEN":
           express = new ShunFengExpress();
         case "YUNDA":
           express = new YunDaExpress();
         case "ZHONG_TONG":
           express = new ZhongTongaExpress();
         default:
           express = new ShunFengExpress();
       }
       return express;
   }
}

定义为静态工厂方法有个缺点:它不能通过类的继承方式,改变创建对象的行为。

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