设计模式-工厂模式


1、工厂模式介绍

工厂模式(Factory Pattern)是Java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时,不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
《设计模式》一书中,工厂模式被分为了三种:简单工厂工厂方法抽象工厂。(不过,在书中作者将简单工厂模式看作是工厂方法模式的一种特例。)


2、需求:模拟用户操作数字钥匙

需求:为了让我们的案例更加贴近实际开发,这里我们来模拟一下互联网汽车中数字钥匙操作的场景,在用户购车后可以开通各种类型的数字钥匙:蓝牙钥匙、手表钥匙、分享钥匙


2.1、原始开发方式

在不考虑任何代码的可扩展性的前提下,只为了尽快满足需求,我们可以这样去设计这个业务的代码结构:

实体类

BluetoothKey——蓝牙钥匙实体类
ShareKey——被分享钥匙实体类(蓝牙钥匙可以被分享)
WatchKey——手表钥匙实体类
ResultUtil——响应结果工具类

/**
 * 蓝牙钥匙实体类
 *
 * @author duckquan
 */
public class BluetoothKey {

    /**
     * 蓝牙钥匙唯一id
     */
    private String id;
    /**
     * 车主
     */
    private String userId;
    /**
     * 对应车辆id
     */
    private String carId;
    /**
     * 钥匙状态:未开通、已开通、已失效......
     */
    private Integer status;

}
/**
 * 分享钥匙实体类
 *
 * @author duckquan
 */
public class ShareKey {

    /**
     * 分享钥匙唯一id
     */
    private String id;
    /**
     * 主人钥匙id
     */
    private String masterKeyId;
    /**
     * 钥匙生效时间
     */
    private Date startDate;
    /**
     * 钥匙失效时间
     */
    private Date endDate;
    /**
     * 钥匙权限:开窗、开空调、打开车门......
     */
    private String keyAuthority;
    /**
     * 钥匙状态:未开通、已开通、已失效......
     */
    private Integer status;
    
}
/**
 * 手表钥匙实体类
 *
 * @author duckquan
 */
public class WatchKey {
    //属性信息省略......    
}
import lombok.Data;

@Data
public class ResultUtil<T> {

    private String code;
    private String message;
    private Object data;

    private static final String successCode = "000";
    private static final String successMessage = "操作成功";

    public static <T> ResultUtil<T> successResult(Object data) {
        ResultUtil<T> result = new ResultUtil();
        result.setCode(successCode);
        result.setMessage(successMessage);
        result.setData(data);
        return result;
    }

}

服务层

BluetoothKeyService——boolean openBlueToothKey(String id)——开通蓝牙钥匙
ShareKeyService——boolean openShareKey(String id)——开通分享钥匙
WatchKeyService——boolean openWatchKey(String id)——开通手表钥匙

/**
 * 蓝牙钥匙
 *
 * @author duckquan
 */
public interface BluetoothKeyService {
    
    /**
     * 开通蓝牙钥匙
     *
     * @param id
     * @return
     */
    boolean openBlueToothKey(String id);

}
import org.springframework.stereotype.Service;

/**
 * 蓝牙钥匙
 *
 * @author duckquan
 */
@Service
public class BluetoothKeyServiceImpl implements BluetoothKeyService {

    @Override
    public boolean openBlueToothKey(String id) {
        System.out.println("【蓝牙钥匙】状态已更改为开通,钥匙id:" + id);
        return true;
    }

}
/**
 * 分享钥匙
 *
 * @author duckquan
 */
public interface ShareKeyService {

    /**
     * 开通分享钥匙
     *
     * @param id
     * @return
     */
    boolean openShareKey(String id);

}
import org.springframework.stereotype.Service;

/**
 * 分享钥匙
 *
 * @author duckquan
 */
@Service
public class ShareKeyServiceImpl implements ShareKeyService {
    
    @Override
    public boolean openShareKey(String id) {
        System.out.println("【分享钥匙】状态已更改为开通,钥匙id:" + id);
        return true;
    }
    
}
/**
 * 手表钥匙
 *
 * @author duckquan
 */
public interface WatchKeyService {

    /**
     * 开通手表钥匙
     *
     * @param id
     * @return
     */
    boolean openWatchKey(String id);

}
import org.springframework.stereotype.Service;

/**
 * 手表钥匙
 *
 * @author duckquan
 */
@Service
public class WatchKeyServiceImpl implements WatchKeyService {
    
    @Override
    public boolean openWatchKey(String id) {
        System.out.println("【手表钥匙】状态已更改为开通,钥匙id:" + id);
        return true;
    }
    
}

控制层

KeyController——ResultUtil<<Object> openKey(String id, Integer type)——

import com.alibaba.fastjson.JSONObject;
import com.example.study.service.BluetoothKeyService;
import com.example.study.service.ShareKeyService;
import com.example.study.service.WatchKeyService;
import com.example.study.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * 钥匙controller层
 * 假装有:@RestController,@RequestMapping("/key")
 * 请忽略:@Component
 *
 * @author duckquan
 */
@Component
public class KeyController {

    @Autowired
    private BluetoothKeyService bluetoothKeyService;

    @Autowired
    private ShareKeyService shareKeyService;

    @Autowired
    private WatchKeyService watchKeyService;


    /**
     * 开通钥匙接口,按照类型开通不同的钥匙
     * 假装有:@PostMapping("/openKey")
     *
     * @param id   钥匙id
     * @param type 1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     * @return
     */
    public ResultUtil<Object> openKey(String id, Integer type) {
        //TODO 这里自行判断参数是否为空
        //--------------------------------
        String resultJson = null;
        if (type == 1) {//蓝牙钥匙
            resultJson = JSONObject.toJSONString(bluetoothKeyService.openBlueToothKey(id));
        }
        if (type == 2) {//分享钥匙
            resultJson = JSONObject.toJSONString(shareKeyService.openShareKey(id));
        }
        if (type == 3) {//手表钥匙
            resultJson = JSONObject.toJSONString(watchKeyService.openWatchKey(id));
        }
        return ResultUtil.successResult(resultJson);
    }

}

测试

import com.example.study.controller.KeyController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * 工厂模式测试类
 */
@SpringBootTest
public class FactoryTest {

    @Autowired //为了方便测试这样子写,请忽略这里引入controller层类的做法
    private KeyController keyController;

    /**
     * 无任何设计模式测试
     * 按照类型的不同开通钥匙
     * 类型:1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     */
    @Test
    public void nonDesignTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.openKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.openKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.openKey("WATCH20240410", 3));
    }

}

对于上面的实现方式,如果我们有想要添加新的钥匙类型时,势必要改动KeyController的代码,违反了开闭原则,而且如果有的钥匙接口出现问题,那么对其进行重构的成本会非常高。
除此之外代码中有一组if分支判断逻辑,现在看起来还可以,但是如果经理几次迭代和拓展,后续if else肯定还会增加,到时候接手这段代码的研发将会十分痛苦。


2.2、简单工厂模式

简单工厂不是一种设计模式,反而比较像是一种编程习惯。简单工厂模式又叫做静态工厂方法模式(static Factory Method pattern),它是通过使用静态方法接收不同的参数来返回不同的实例对象。
实现方式:定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
适用场景:需要创建的对象较少、客户端不关心对象的创建过程。

简单工厂原理

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能
  • 具体产品 :实现或者继承抽象产品的子类
  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

简单工厂重构代码
Service

/**
 * 数字钥匙service
 *
 * @author duckquan
 */
public interface KeyService {

    /**
     * 开通数字钥匙
     *
     * @param id
     * @return
     */
    boolean openKey(String id);

}
import org.springframework.stereotype.Service;

/**
 * 蓝牙钥匙
 *
 * @author duckquan
 */
@Service
public class BluetoothKeyServiceImpl implements KeyService {

    @Override
    public boolean openKey(String id) {
        System.out.println("【蓝牙钥匙】状态已更改为开通,钥匙id:" + id);
        return true;
    }

}
import org.springframework.stereotype.Service;

/**
 * 分享钥匙
 *
 * @author duckquan
 */
@Service
public class ShareKeyServiceImpl implements KeyService {

    @Override
    public boolean openKey(String id) {
        System.out.println("【分享钥匙】状态已更改为开通,钥匙id:" + id);
        return true;
    }

}
import org.springframework.stereotype.Service;

/**
 * 手表钥匙
 *
 * @author duckquan
 */
@Service
public class WatchKeyServiceImpl implements KeyService {

    @Override
    public boolean openKey(String id) {
        System.out.println("【手表钥匙】状态已更改为开通,钥匙id:" + id);
        return true;
    }

}

Factory

import com.example.study.service.BluetoothKeyServiceImpl;
import com.example.study.service.KeyService;
import com.example.study.service.ShareKeyServiceImpl;
import com.example.study.service.WatchKeyServiceImpl;
import org.springframework.stereotype.Component;

/**
 * 数字钥匙工厂:生成具体的数字钥匙service
 *
 * @author duckquan
 */
@Component
public class KeySimpleFactory {

    /**
     * 获取数字钥匙service实例
     *
     * @param type 类型:1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     * @return
     */
    public static KeyService getInstance(Integer type) {
        KeyService service = null;
        if (type == 1) {
            service = new BluetoothKeyServiceImpl();
        }
        if (type == 2) {
            service = new ShareKeyServiceImpl();
        }
        if (type == 3) {
            service = new WatchKeyServiceImpl();
        }
        return service;
    }

}

Controller

import com.alibaba.fastjson.JSONObject;
import com.example.study.design.factory.KeySimpleFactory;
import com.example.study.service.KeyService;
import com.example.study.utils.ResultUtil;
import org.springframework.stereotype.Component;


/**
 * 钥匙controller层
 * 假装有:@RestController,@RequestMapping("/key")
 * 请忽略:@Component
 *
 * @author duckquan
 */
@Component
public class KeyController {

    /**
     * 开通钥匙接口,按照类型开通不同的钥匙
     * 假装有:@PostMapping("/openKey")
     *
     * @param id   钥匙id
     * @param type 1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     * @return
     */
    public ResultUtil<Object> openKey(String id, Integer type) {
        //TODO 这里自行判断参数是否为空
        //--------------------------------
        KeyService service = KeySimpleFactory.getInstance(type);
        return ResultUtil.successResult(JSONObject.toJSONString(service.openKey(id)));
    }

}

测试

import com.example.study.controller.KeyController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * 工厂模式测试类
 */
@SpringBootTest
public class FactoryTest {

    @Autowired //为了方便测试这样子写,请忽略这里引入controller层类的做法
    private KeyController keyController;

    /**
     * 无任何设计模式测试
     * 按照类型的不同开通钥匙
     * 类型:1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     */
    @Test
    public void nonDesignTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.openKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.openKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.openKey("WATCH20240410", 3));
    }

    /**
     * 简单工厂模式测试
     */
    @Test
    public void simpleFactoryTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.openKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.openKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.openKey("WATCH20240410", 3));
    }

}

简单工厂模式总结
优点:封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
缺点:增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。


2.3、工厂方法模式

工厂方法模式(Factory Method pattern)属于创建型模式。
概念:定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法模式的目的很简单,就是封装对象创建的过程,提升创建对象方法的可复用性。
工厂方模式的主要角色:

  • 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

我们直接来看看工厂方法模式的UML图:
在这里插入图片描述


2.3.1、工厂方法模式重构代码

为了提高代码扩展性,我们需要将简单工厂中的if分支逻辑去掉,通过增加抽象工厂(生产工厂的工厂)的方式,让具体工厂去进行实现,由具体工厂来决定实例化哪一个具体的产品对象。

抽象工厂

import com.example.study.service.KeyService;

/**
 * 数字钥匙抽象工厂
 *
 * @author duckquan
 */
public interface KeyMethodFactory {

    /**
     * 获取具体工厂实例
     *
     * @return
     */
    KeyService getInstance();

}

具体工厂

import com.example.study.service.BluetoothKeyServiceImpl;
import com.example.study.service.KeyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 蓝牙钥匙工厂
 *
 * @author duckquan
 */
@Component
public class BluetoothKeyFactory implements KeyMethodFactory {

    @Autowired
    private BluetoothKeyServiceImpl bluetoothKeyService;

    @Override
    public KeyService getInstance() {
        return bluetoothKeyService;
    }

}
import com.example.study.service.KeyService;
import com.example.study.service.ShareKeyServiceImpl;
import org.springframework.stereotype.Component;

/**
 * 分享钥匙工厂
 *
 * @author duckquan
 */
@Component
public class ShareKeyFactory implements KeyMethodFactory {

    @Override
    public KeyService getInstance() {
        return new ShareKeyServiceImpl();
    }

}
import com.example.study.service.KeyService;
import com.example.study.service.WatchKeyServiceImpl;
import org.springframework.stereotype.Component;

/**
 * 手表钥匙工厂
 *
 * @author duckquan
 */
@Component
public class WatchKeyServiceFactory implements KeyMethodFactory {

    @Override
    public KeyService getInstance() {
        return new WatchKeyServiceImpl();
    }

}

这里可以增加一个用来获取具体工厂的类,降低工厂创建与业务代码的耦合,我们姑且称这个类为工厂的工厂。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * 用简单方法模式实现: 工厂的工厂,作用是不需要每次创建新的工厂对象
 *
 * @author duckquan
 */
@Component
public class KeyMethodFactoryMap {

    @Autowired
    private BluetoothKeyFactory bluetoothKeyFactory;

    @Autowired
    private ShareKeyFactory shareKeyFactory;

    @Autowired
    private WatchKeyServiceFactory watchKeyServiceFactory;

    //存放具体工厂对象的map集合
    private static final Map<Integer, KeyMethodFactory> cachedFactories = new HashMap<>();

    @PostConstruct
    public void init() {
        cachedFactories.put(1, bluetoothKeyFactory);
        cachedFactories.put(2, shareKeyFactory);
        cachedFactories.put(3, watchKeyServiceFactory);
    }

    /**
     * 获得具体工厂类
     *
     * @param type
     * @return
     */
    public KeyMethodFactory getFactory(Integer type) {
        return cachedFactories.get(type);
    }

}

Controller

import com.alibaba.fastjson.JSONObject;
import com.example.study.design.factory.KeyMethodFactory;
import com.example.study.design.factory.KeyMethodFactoryMap;
import com.example.study.design.factory.KeySimpleFactory;
import com.example.study.service.KeyService;
import com.example.study.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * 钥匙controller层
 * 假装有:@RestController,@RequestMapping("/key")
 * 请忽略:@Component
 *
 * @author duckquan
 */
@Component
public class KeyController {

    @Autowired
    private KeyMethodFactoryMap keyMethodFactoryMap;

    /**
     * 简单工厂模式-开通钥匙接口,按照类型开通不同的钥匙
     * 假装有:@PostMapping("/openKey")
     *
     * @param id   钥匙id
     * @param type 1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     * @return
     */
    public ResultUtil<Object> openKey(String id, Integer type) {
        //TODO 这里自行判断参数是否为空
        //--------------------------------
        KeyService service = KeySimpleFactory.getInstance(type);
        return ResultUtil.successResult(JSONObject.toJSONString(service.openKey(id)));
    }

    /**
     * 工厂方法模式-开通钥匙接口,按照类型开通不同的钥匙
     * 假装有:@PostMapping("/methodFactoryOpenKey")
     *
     * @param id   钥匙id
     * @param type 1-蓝牙钥匙,2-分享钥匙,3-手表钥匙
     * @return
     */
    public ResultUtil<Object> methodFactoryOpenKey(String id, Integer type) {
        //TODO 这里自行判断参数是否为空
        //--------------------------------
        //获取工厂
        KeyMethodFactory factory = keyMethodFactoryMap.getFactory(type);
        //获取产品
        KeyService service = factory.getInstance();
        return ResultUtil.successResult(JSONObject.toJSONString(service.openKey(id)));
    }

}

测试

import com.example.study.controller.KeyController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * 工厂模式测试类
 */
@SpringBootTest
public class FactoryTest {

    @Autowired //为了方便测试这样子写,请忽略这里引入controller层类的做法
    private KeyController keyController;

    /**
     * 简单工厂模式测试
     */
    @Test
    public void simpleFactoryTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.openKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.openKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.openKey("WATCH20240410", 3));
    }

    /**
     * 工厂方法模式测试
     * 工厂模式可以结合单例模式一起使用,这里可以在工厂类上加上注解,把实例交给Spring管理(Spring管理的实例默认是单例)
     */
    @Test
    public void methodFactoryTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.methodFactoryOpenKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.methodFactoryOpenKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.methodFactoryOpenKey("WATCH20240410", 3));
    }

}

现在我们的代码已经基本上符合了开闭原则,当有新增的产品时,我们需要做的事情包括:

  1. 创建新的产品类,并且让该产品实现抽象产品接口
  2. 创建产品类对应的具体工厂,并让具体工厂实现抽象工厂
  3. 将新的具体工厂对象,添加到KeyMethodFactoryMapcachedFactories中即可,需要改动的代码改动的非常少。

工厂方法模式总结
优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则

缺点:

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度

什么时候使用工厂方法模式

  • 需要使用很多重复代码创建对象时,比如,DAO层的数据对象、API层的VO对象等。
  • 创建池化对象时,比如,连接池对象、线程池对象、日志对象等。这些对象的特性是:有限、可重用,使用工厂方法模式可以有效节约资源。
  • 希望隐藏对象的真实类型时,比如,不希望使用者知道对象的真实构造函数参数等。
  • 创建对象要访问外部信息或资源时,比如,读取数据库字段,获取访问授权token信息,配置文件等。
  • 创建需要统一管理生命周期的对象时,比如,会话信息、用户网页浏览轨迹对象等。

3、抽象工厂模式

抽象工厂模式比工厂方法模式的抽象程度更高,在工厂方法模式中每一个具体工厂只需要生产一种具体产品,但是在抽象工厂模式中一个具体工厂可以生产一组相关的具体产品,这样一组产品被称为产品族,产品族中的每一个产品都分属于某一个产品继承等级结构。

产品等级结构与产品族
为了更好的理解抽象工厂,我们这里先引入两个概念:

  • 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机海信电视机TCL电视机,则抽象电视机具体品牌的电视机之间构成了一个产品等级结构抽象电视机是父类,而具体品牌的电视机是其子类。
  • 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机海尔电冰箱海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
    在这里插入图片描述

在上图中,每一个具体工厂可以生产属于一个产品族的所有产品,例如海尔工厂生产海尔电视机海尔空调海尔冰箱,所生产的产品又位于不同的产品等级结构中。如果使用工厂方法模式,上图所示的结构需要提供9个具体工厂,而使用抽象工厂模式只需要提供3个具体工厂,极大减少了系统中类的个数。

抽象工厂模式描述
抽象工厂模式(Abstract Factory Pattern)原始定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
抽象工厂模式为创建一组对象提供了解决方案,与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,而是负责创建一个产品族。如下图:
在这里插入图片描述


3.1、抽象工厂模式原理

在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法,用于产生多种不同类型的产品,这些产品构成了一个产品族。
在这里插入图片描述
抽象工厂模式的主要角色如下:

  • 抽象工厂(Abstract Factory):它声明了一种用于创建一族产品的方法,每一个方法对应一种产品
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系

抽象工厂

/**
 * 抽象工厂:在一个抽象工厂中可以声明多个工厂方法,用于创建不同类型的产品
 *
 * @author duckquan
 */
public interface AppliancesFactory {

    /**
     * 创建不同品牌电视机(海尔、海信、TCL)
     *
     * @return
     */
    AbstractTV createTV();

    /**
     * 创建不同品牌冰箱(海尔、海信、TCL)
     *
     * @return
     */
    AbstractFreezer createFreezer();

}

具体工厂:每一个具体工厂的方法,可以返回一个特定的产品对象,而同一个具体的工厂所创建的产品对象构成了一个产品族

/**
 * 具体工厂-海尔工厂
 *
 * @author duckquan
 */
public class HairFactory implements AppliancesFactory {

    @Override
    public AbstractTV createTV() {
        return new HairTV();
    }

    @Override
    public AbstractFreezer createFreezer() {
        return new HairFreezer();
    }

}
/**
 * 具体工厂-海信工厂
 *
 * @author duckquan
 */
public class HisenseFactory implements AppliancesFactory {

    @Override
    public AbstractTV createTV() {
        return new HisenseTV();
    }

    @Override
    public AbstractFreezer createFreezer() {
        return new HisenseFreezer();
    }

}

抽象产品

/**
 * 抽象产品-电视机
 * 
 * @author duckquan
 */
public interface AbstractTV {
}
/**
 * 抽象产品-冰箱
 *
 * @author duckquan
 */
public interface AbstractFreezer {
}

具体产品

/**
 * 具体产品-海尔电视
 *
 * @author duckquan
 */
public class HairTV implements AbstractTV {
}
/**
 * 具体产品-海信电视
 *
 * @author duckquan
 */
public class HisenseTV implements AbstractTV {
}
/**
 * 具体产品-海尔冰箱
 *
 * @author duckquan
 */
public class HairFreezer implements AbstractFreezer {
}
/**
 * 具体产品-海信冰箱
 *
 * @author duckquan
 */
public class HisenseFreezer implements AbstractFreezer {
}

客户端

import lombok.Data;

/**
 * 客户端
 *
 * @author duckquan
 */
@Data
public class Client {

    private AbstractTV tv;

    private AbstractFreezer freezer;

    public Client(AppliancesFactory factory) {
        //在客户端看来就是使用抽象工厂来生产家电
        this.tv = factory.createTV();
        this.freezer = factory.createFreezer();
    }

}

测试

import com.example.study.controller.KeyController;
import com.example.study.design.factory.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * 工厂模式测试类
 */
@SpringBootTest
public class FactoryTest {

    @Autowired //为了方便测试这样子写,请忽略这里引入controller层类的做法
    private KeyController keyController;

    /**
     * 简单工厂模式测试
     */
    @Test
    public void simpleFactoryTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.openKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.openKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.openKey("WATCH20240410", 3));
    }

    /**
     * 工厂方法模式测试
     * 工厂模式可以结合单例模式一起使用,这里可以在工厂类上加上注解,把实例交给Spring管理(Spring管理的实例默认是单例)
     */
    @Test
    public void methodFactoryTest() {
        //蓝牙钥匙开通
        System.out.println("蓝牙钥匙:" + keyController.methodFactoryOpenKey("BLUE20240410", 1) + "\n");
        //分享钥匙开通
        System.out.println("分享钥匙:" + keyController.methodFactoryOpenKey("SHARE20240410", 2) + "\n");
        //手表钥匙开通
        System.out.println("手表钥匙:" + keyController.methodFactoryOpenKey("WATCH20240410", 3));
    }

    /**
     * 抽象工厂模式测试
     */
    @Test
    public void abstractFactoryTest() {
        Client client = new Client(new HairFactory());
        AbstractTV tv = client.getTv();
        System.out.println(tv);
        AbstractFreezer freezer = client.getFreezer();
        System.out.println(freezer);
    }

}

3.2、抽象工厂模式总结

从上面代码实现中我们可以看出,抽象工厂模式向使用(客户)方隐藏了下列变化

  • 程序所支持的实例集合(具体工厂)的数目
  • 当前是使用的实例集合中哪一个实例
  • 在任意给定时刻被实例化的具体类型

所以说,在理解抽象工厂模式原理时,你一定要牢牢记住如何找到某一个类产品的正确共性功能这个重点。
抽象工厂模式优点

  • 对于不同产品系列有比较多共性特征时,可以使用抽象工厂模式,有助于提升组件的复用性
  • 当需要提升代码的扩展性并降低维护成本时,把对象的创建和使用过程分开,能有效地将代码统一到一个级别上
  • 解决跨平台带来的兼容性问题

抽象工厂模式缺点
增加新的产品等级结构麻烦,需要对原有结构进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大不变,违背了开闭原则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值