DesignPattern - 工厂模式【创建型】

1,182 阅读9分钟

欢迎关注微信公众号:FSA全栈行动 👋

一、工厂模式介绍

工厂模式提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

  • 例子:
    • 需要购买一辆车,不用管车辆如何组装,且可以购买不同类型的汽车,比如轿车、SUV、跑车,直接去 4s 店购买就行(4s 店就是工厂)
    • 工厂生产电脑,除了 A 品牌、还可以生产 B、C、D 品牌电脑
    • 支付业务开发,会统一下单和支付接口,具体的支付实现可以是微信、支付、银行卡等
  • 分类:
    • 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单一,可扩展性较差。
    • 工厂方法模式:通过实现类 实现相应的方法来决定相应的返回结果,这种方式的可扩展性比较强。
    • 抽象工厂模式:基于上述两种模式的扩展,且支持细化产品。
  • 应用场景:
    • 解耦:分离职责,把复杂对象的创建和使用的过程分开
    • 复用代码,降低维护成本:
      • 如果对象创建复杂且多处需要用到,如果每处都进行编写,则很多重复代码,如果业务逻辑发生了改变,需要四处修改
      • 使用工厂模式统一创建,则只要修改工厂类即可,降低成本

提示:工厂模式适合复杂对象的创建

二、工厂模式代码实现

1、简单工厂模式

简单工厂可以根据不同参数返回不同类的实例,需要专门定义一个工厂类来负责创建其他类的实例,被创建实例通常都具有共同的父类或接口;简单工厂又称 静态工厂方法,可通过类名直接调用 ,而且只需要传入简单的参数即可。

  • 核心组成
    • Factory:工厂类,简单工厂模式的核心,它负责实现创建所有实例的内部逻辑
    • IProduct:抽象产品类,简单工厂模式所创建的所有对象的父类,描述所有实例所共有的公共接口
    • Product:具体产品类,是简单工厂模式的创建目标
  • 实现步骤
    • 创建抽象产品类,里面有产品的抽象方法,由具体的产品类去实现
    • 创建具体产品类,继承【创建抽象产品类】,并实现具体方法
    • 创建工厂类,提供一个静态方法(createXXX)用来生产产品,只需要传入你想要的产品标识(如:产品名称)
  • 优点:将对象的创建和对象本身业务处理分离,可以降低系统的耦合度,使得两者修改起来都相对容易。
  • 缺点:工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的

创建抽象产品类:

/**
 * 抽象产品
 *
 * @author GitLqr
 */
public interface IPay {
	/**
	 * 下单功能
	 */
	void order();
}

创建具体产品类:

/**
 * 具体产品:微信支付
 *
 * @author GitLqr
 */
public class WechatPay implements IPay {
	@Override
	public void order() {
		System.out.println("微信 下单");
	}
}

/**
 * 具体产品:支付宝支付
 *
 * @author GitLqr
 */
public class AliPay implements IPay {
	@Override
	public void order() {
		System.out.println("支付宝 下单");
	}
}

创建工厂类:

/**
 * 工厂类
 *
 * @author GitLqr
 */
public class PayFactory {
	/**
	 * 根据参数 返回对应的支付对象
	 */
	public static IPay createPay(String payType) {
		switch (payType) {
		case "alipay":
			return new AliPay();
		case "wechat":
			return new WechatPay();
		default:
			return null;
		}
	}
}

使用:

public static void main(String[] args) {
    // 简单工厂
    // IPay pay = PayFactory.createPay("alipay");
    IPay pay = PayFactory.createPay("wechat");
    pay.order();
}

2、工厂方法模式

工厂方法又称 工厂模式,是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。通过工厂父类定义负责创建产品的公共接口,通过子类来确定所需要创建的类型。

  • 核心组成
    • IProduct:抽象产品类,描述所有实例所共有的公共接口
    • Product:具体产品类,实现抽象产品类的接口
    • IFactory:抽象工厂类,描述具体工厂的公共接口
    • Factory:具体工厂类,实现抽象工厂类的接口,创建具体产品对象
  • 优点:
    • 相比简单工厂而言,工厂方法具有更多的可扩展性和复用性,同时也增强了代码的可读性
    • 将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化哪一个类
    • 符合开闭原则,增加一个产品类,只需要实现其他具体的产品类和具体的工厂类
    • 符合单一职责原则,每个工厂只负责生产对应的产品
    • 使用者只需要知道产品的抽象类,无须关心产品的具体实现类,满足迪米特法则、依赖倒置原则和里氏替换原则
  • 缺点:
    • 每个产品需要有对应的具体工厂和具体产品类,即每增加一个产品,都至少会增加 2 个类
    • 使用者必须知道具体的工厂实现类

创建抽象工厂类:

/**
 * 抽象工厂类
 *
 * @author GitLqr
 */
public interface IPayFactory {
	public IPay getPay();
}

创建具体工厂类:

/**
 * 具体工厂类:创建 支付宝支付 对象
 *
 * @author GitLqr
 */
public class AliPayFactory implements IPayFactory {
	@Override
	public IPay getPay() {
		return new AliPay();
	}
}

/**
 * 具体工厂类:创建 微信支付 对象
 *
 * @author GitLqr
 */
public class WechatPayFactory implements IPayFactory {
	@Override
	public IPay getPay() {
		return new WechatPay();
	}
}

使用:

public static void main(String[] args) {
    // 工厂方法
    // IPayFactory payFactory = new AliPayFactory();
    IPayFactory payFactory = new WechatPayFactory();
    payFactory.getPay().order();
}

补充:如果后续需要增加第三种支付方式,比如银联支付,那么只需要再扩展出具体产品 UnionPay,以及具体工厂 UnionPayFactory 即可,而无需改动原有的类。

3、抽象工厂模式

抽象工厂模式是基于上述两种模式的拓展,是工厂方法模式的升级版,当需要创建的产品有多个产品线时使用抽象工厂模式是比较好的选择。

  • 背景:
    • 工厂方法模式引入工厂等级结构,解决了简单工厂模式中工厂类职责过重的问题
    • 但工厂方法模式中每个工厂只创建一类具体产品类的对象,后续发展可能会导致工厂类过多,因此将一些相关的具体类组成一个”具体类族“,由同一个工厂来统一生产,强调的是一系列相关的产品对象!!
  • 优点:
    • 当一个产品族中的多个对象被设计成一起工作时,它能保证使用方始终只使用一个产品族中的对象
  • 缺点:
    • 产品族扩展困难,要增加一个系列的某一产品,既要在抽象工厂和超级工厂类里修改代码,不是很符合开闭原则
    • 增加了系统的抽象性和理解难度

补充:当抽象工厂模式中每一个具体工厂类只创建一个产品对象时,抽象工厂模式退化成工厂方法模式。

1)推导过程(工厂方法 --> 抽象工厂 )

假设 "产品" 现在不只有 支付,还需要有 退款,提现 等,这时,"产品" 的【抽象产品类】和【具体产品类】将拓展为如下所示:

支付(IPay)退款(IRefund)提现(ICashout)
支付宝AliPayAliRefundAliCashout
微信WechatPayWechatRefundWechatCashout
银联UnionPayUnionRefundUnionCashout

根据 工厂方法模式 的核心要求(一种工厂 对应生成 一种产品),那么对应的【抽象工厂类】和【具体工厂类】将拓展为如下所示:

支付(IPayFactory)退款(IRefundFactory)提现(ICashoutFactory)
支付宝AliPayFactoryAliRefundFactoryAliCashoutFactory
微信WechatPayFactoryWechatRefundFactoryWechatCashoutFactory
银联UnionPayFactoryUnionRefundFactoryUnionCashoutFactory

这时就会发现,严格按照工厂方法模式的要求会有如下弊端:

  1. 工厂类太多了,整体感觉工程文件数量很冗余
  2. 对使用者不友好,需要知道的 "工厂" 有点多

其实这些 "产品"(支付、退款、提现...)都是属于一类(订单相关), 因此,可以对【抽象工厂类】进行增强,由一个【抽象工厂类】来负责生产一类 "产品",例如可以创建 IOrderFactory,汇总 IPayFactory、IRefundFactory、ICashoutFactory 的创建方法并对它们进行取缔。于是,使用了抽象工厂模式后,对应的【抽象工厂类】和【具体工厂类】将拓展为如下所示:

支付、退款、提现(IOrderFactory)
支付宝AliOrderFactory
微信WechatOrderFactory
银联UnionOrderFactory

注意:抽象工厂 相比 工厂方法,就是增强了抽象工厂类,同时思想上也有了重大变化,让抽象工厂类不再局限于一种产品,而是一系列产品。

2)代码实现

创建抽象产品类:

/**
 * 抽象产品
 *
 * @author GitLqr
 */
public interface IRefund {
	/**
	 * 退款
	 */
	void refund();
}

创建具体产品类:

/**
 * 具体产品:支付宝退款
 *
 * @author GitLqr
 */
public class AliRefund implements IRefund {
	@Override
	public void refund() {
		System.out.println("支付宝 退款");
	}
}
/**
 * 具体产品:微信退款
 *
 * @author GitLqr
 */
public class WechatRefund implements IRefund {
	@Override
	public void refund() {
		System.out.println("微信 退款");
	}
}

创建抽象工厂类【增强版】:

/**
 * 抽象工厂类【增强版】
 *
 * @author GitLqr
 */
public interface IOrderFactory {
	IPay getPay();

	IRefund getRefund();
}

创建具体工厂类【增强版】:

/**
 * 具体工厂类:创建 支付宝 订单相关的一系列产品功能
 *
 * @author GitLqr
 */
public class AliOrderFactory implements IOrderFactory {
	@Override
	public IPay getPay() {
		return new AliPay();
	}

	@Override
	public IRefund getRefund() {
		return new AliRefund();
	}
}
/**
 * 具体工厂类:创建 微信 订单相关的一系列产品功能
 *
 * @author GitLqr
 */
public class WechatOrderFactory implements IOrderFactory {
	@Override
	public IPay getPay() {
		return new WechatPay();
	}

	@Override
	public IRefund getRefund() {
		return new WechatRefund();
	}
}

还可以借鉴简单工厂模式,创建一个"超级工厂类",根据不同参数获取不同的具体工厂,方便使用者使用:

public class OrderFactoryProducer {

	public static IOrderFactory getFactory(String type) {
		switch (type) {
		case "alipay":
			return new AliOrderFactory();
		case "wechat":
			return new WechatOrderFactory();
		default:
			return null;
		}
	}
}

说明:【简单工厂模式】的最大缺点就是负责了所有具体产品的创建,而这里则不同,具体产品的创建已交由具体工厂去创建,"超级工厂类" 只负责找到合适的具体工厂,与产品没有直接关系。当然,”超级工厂类“这种方式方式还是不符合开闭原则的,需自己权衡利弊,个人感觉至少比让使用者直接使用具体工厂要好控制一些。

使用:

public static void main(String[] args) {
    // 抽象工厂
    // IOrderFactory orderFactory = OrderFactoryProducer.getFactory("alipay");
    IOrderFactory orderFactory = OrderFactoryProducer.getFactory("wechat");
    orderFactory.getPay().order();
    orderFactory.getRefund().refund();
}

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~