03 设计模式之工厂模式

318 阅读7分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

1 背景

在 Java 中,提倡面向接口编程,接口可以定义约束实现类的行为,外部调用通过接口进行调用,达到了封装隔离的效果,外部不知道内部的具实现。而且使用接口编程,提高了可维护性和可扩展性。

 package com.chenpi.simplefactory;
 ​
 /**
  * @Description
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class ChenPiMain {
 ​
   public static void main(String[] args) {
     PayService payService = new AliPayService();
     payService.pay();
   }
 ​
 }
 ​
 interface PayService {
 ​
   void pay();
 }
 ​
 class AliPayService implements PayService {
 ​
   @Override
   public void pay() {
     System.out.println("使用支付宝支付...");
   }
 }

以上代码其实有问题的,本来是要面向接口编程,但是客户端却知道了接口实现类,违背了封装隔离原则。那就不要让客户端去创建具体实现类,那接口实现类又该如何获取呢?

2 工厂模式

工厂模式可以解决以上问题。工厂模式属于创建型模式,它提供了一个创建对象实例的功能,而不关心其具体的实现。

工厂模式主要有工厂方法模式,抽象工厂模式两类。

2.1 工厂方法(Factory Method)

定义一个用于创建对象的接口,接口实现类决定创建哪一个类的实例,工厂方法使一个类的实例化延迟到接口的实现子类上。

工厂方法模式的结构图如下:

  • Product:定义工厂方法所创建的对象的接口。
  • ConcreteProduct:Product 接口的实现类。
  • XXFactory:工厂抽象类,里面定义一个抽象方法,返回被创建的对象的接口。
  • ConcreteXXFactory:工厂抽象方法的实现类,负责工厂方法的实例创建。

image-20210801231833644.png

还是以开头的例子演示,工厂方法创建的实例一般都有共同的父亲,所以需要定义一个接口。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 被创建实例的接口
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public interface PayService {
 ​
   void pay();
 }

再定义接口的实现类,也就是工厂实际创建的对象。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 被创建实例接口的实现类
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class AliPayService implements PayService {
 ​
   @Override
   public void pay() {
     System.out.println("使用支付宝支付...");
   }
 }

工厂抽象类,定义约束了需要创建的对象接口,具体实现由子类负责。工厂类的名称一般为模块名称+Factory

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 工厂抽象类
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public abstract class PayServiceFactory {
 ​
   public abstract PayService getPayService();
 }

工厂接口的实现类,负责创建实际需要的对象实例。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 工厂接口实现类
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class AliPayServiceFactory extends PayServiceFactory {
 ​
   @Override
   public PayService getPayService() {
     return new AliPayService();
   }
 }

客户端使用工厂获取对象,进行使用。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class ChenPiClient {
 ​
   public static void main(String[] args) {
     PayServiceFactory factory = new AliPayServiceFactory();
     PayService payService = factory.getPayService();
     payService.pay();
   }
 }
 ​
 // 输出结果
 使用支付宝支付...

如果此时又要新增一个支付方式,例如微信支付,那么只需要新增一个微信支付相关的业务类,以及对象的工厂实现类即可。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 被创建实例接口的实现类
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class WeChatPayService implements PayService {
 ​
   @Override
   public void pay() {
     System.out.println("使用微信支付...");
   }
 }
 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 工厂接口实现类
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class WeChatPayServiceFactory extends PayServiceFactory {
 ​
   @Override
   public PayService getPayService() {
     return new WeChatPayService();
   }
 }

客户端只需要更好具体的工厂实现类即可。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class ChenPiClient {
 ​
   public static void main(String[] args) {
     PayServiceFactory factory = new WeChatPayServiceFactory();
     PayService payService = factory.getPayService();
     payService.pay();
   }
 }
 ​
 // 输出结果
 使用微信支付...

当然这些结构不一定都是定死的,设计模式表达的是一种思想,思路,具体的落地方案都是得根据实际情况而定的,比如工厂方法模式不一定要定义工厂接口以及多个实现类,也可以直接定义一个工厂具体类,根据客户端传入的参数或者读取其他数据(例如配置文件,数据库等)选择创建具体的实例即可。

 package com.chenpi.factorymethod;
 ​
 /**
  * @Description 工厂方法
  * @Author 陈皮
  * @Date 2021/8/1
  * @Version 1.0
  */
 public class OnlinePayServiceFactory {
 ​
   public PayService getPayService(String type) {
     if ("AliPay".equals(type)) {
       return new AliPayService();
     } else if ("WeChatPay".equals(type)) {
       return new WeChatPayService();
     }
     return null;
   }
 }

2.2 抽象工厂(Abstract Factory)

工厂方法一般是创建单个类的实例,或者没有内在联系的类的实例。而且抽象工厂一般是创建有关联,或者相互依赖的类的实例,可以理解为生产产品簇的工厂。


抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的实现类。

  • AbstractFactory:抽象工厂,定义约束创建一系列产品对象的操作接口。
  • ConcreteFactory:抽象工厂的具体实现类,负责一系列有相互关系的类的对象的创建。
  • AbstractProduct:定义一系列产品对象的接口。
  • ConcreteProduct:产品接口的具体实现类。

image-20210803220043574.png

举个例子,假如你要开个电脑组装公司,业务是帮一些电脑小白组装电脑,为简单方便讲解,我们假设组装一个电脑只需要 CPU,显示屏以及主板。那组装总公司(抽象工厂)定义约束了组装一个电脑需要哪些组件接口(一系列对象接口),但是具体牌子的电脑组件由不同的子公司(接口实现类)来实现,而且这些组件是有互相依赖关系的。总而言之,抽象工厂就是起到一个定义约束的作用,实现子类必须要接口定义来创建一系列对象。

首先定义一个抽象工厂接口,在里面定义约束创建一系列电脑组件的接口。

 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 抽象工厂
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public interface AbstractFactory {
 ​
   CPU getCPU();
 ​
   Mainboard getMainboard();
 }

再定义抽象工厂接口的实现类,也就是具体的组装电脑方案实现。

 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 抽象工厂实现类A
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class ConcreteFactoryA implements AbstractFactory {
 ​
   @Override
   public CPU getCPU() {
     return new ConcreteCPUA();
   }
 ​
   @Override
   public Mainboard getMainboard() {
     return new ConcreteMainboardA();
   }
 }
 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 抽象工厂实现类B
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class ConcreteFactoryB implements AbstractFactory {
 ​
   @Override
   public CPU getCPU() {
     return new ConcreteCPUB();
   }
 ​
   @Override
   public Mainboard getMainboard() {
     return new ConcreteMainboardB();
   }
 }

CPU 产品接口定义,以及 CPU 具体实现类定义。

 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 抽象CPU接口
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public interface CPU {
 ​
 }
 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 具体品牌A的CPU
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class ConcreteCPUA implements CPU {
 ​
 }
 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 具体品牌B的CPU
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class ConcreteCPUB implements CPU {
 ​
 }

主板产品接口定义,以及主板接口的具体实现类定义。

 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 抽象主板接口
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public interface Mainboard {
 ​
 }
 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 品牌A的主板
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class ConcreteMainboardA implements Mainboard {
 ​
 }
 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description 品牌B的主板
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class ConcreteMainboardB implements Mainboard {
 ​
 }

编写客户端,进行验证,假设选择方案 A 进行验证。

 package com.chenpi.abstractfactory;
 ​
 /**
  * @Description
  * @Author 陈皮
  * @Date 2021/8/3
  * @Version 1.0
  */
 public class Client {
 ​
   public static void main(String[] args) {
     AbstractFactory factory = new ConcreteFactoryA();
     CPU cpu = factory.getCPU();
     Mainboard mainboard = factory.getMainboard();
 ​
     System.out.println("CPU:" + cpu);
     System.out.println("Mainboard:" + mainboard);
   }
 }
 ​
 // 输出结果
 CPU:com.chenpi.abstractfactory.ConcreteCPUA@4554617c
 Mainboard:com.chenpi.abstractfactory.ConcreteMainboardA@74a14482

通过抽象工厂模式,确保了一个具体的工厂只生产有互相关联,互相依赖的产品簇,不会出现混乱搭配的情况。


我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章喔!

本次分享到此结束啦~~

如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!