当前位置: 首页 > article >正文

01 设计模式-创造型模式-工厂模式

  • 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,它提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离。

  • 工厂模式提供了一种创建对象的方式,而无需指定要创建的具体类。

  • 通过使用工厂模式,可以将对象的创建逻辑封装在一个工厂类中,而不是在客户端代码中直接实例化对象,这样可以提高代码的可维护性和可扩展性。

设计模式,最近持续更新中,如需要请关注

如果你觉得我分享的内容或者我的努力对你有帮助,或者你只是想表达对我的支持和鼓励,请考虑给我点赞、评论、收藏。您的鼓励是我前进的动力,让我感到非常感激。

文章目录

  • 1 概要
  • 2 实现
  • 3 Demo代码
  • 4 开发案例
    • 4.1 算法执行服务里,不同的任务数据来源不同,执行脚本类型不同,结果处理不同,使用工厂模式
    • 4.2 网络还原时,不同的采集数据,解析处理方式不同

1 概要

意图
定义一个创建对象的接口,让其子类决定实例化哪一个具体的类。工厂模式使对象的创建过程延迟到子类。

主要解决
接口选择的问题。

何时使用
当我们需要在不同条件下创建不同实例时。

如何解决
通过让子类实现工厂接口,返回一个抽象的产品。

关键代码
对象的创建过程在子类中实现。

应用实例

  1. 汽车制造:你需要一辆汽车,只需从工厂提货,而不需要关心汽车的制造过程及其内部实现。
  2. Hibernate:更换数据库时,只需更改方言(Dialect)和数据库驱动(Driver),即可实现对不同数据库的切换。

优点

  1. 调用者只需要知道对象的名称即可创建对象。
  2. 扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
  3. 屏蔽了产品的具体实现,调用者只关心产品的接口。

缺点
每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。

使用场景

  1. 日志记录:日志可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志的位置。
  2. 数据库访问:当用户不知道最终系统使用哪种数据库,或者数据库可能变化时。
  3. 连接服务器的框架设计:需要支持 “POP3”、“IMAP”、“HTTP” 三种协议,可以将这三种协议作为产品类,共同实现一个接口。
  4. 在算法执行服务中,每个任务需要处理的数据来源不同,根据数据类型创建对应的数据出来handle类,不用关心handle里的内部处理逻辑

注意事项
工厂模式适用于生成复杂对象的场景。如果对象较为简单,通过 new 即可完成创建,则不必使用工厂模式。使用工厂模式会引入一个工厂类,增加系统复杂度。

结构
工厂模式包含以下几个主要角色:

  • 抽象产品(Abstract Product):定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口,规定了产品对象的共同方法。
  • 具体产品(Concrete Product):实现了抽象产品接口,定义了具体产品的特定行为和属性。
  • 抽象工厂(Abstract Factory):声明了创建产品的抽象方法,可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。
  • 具体工厂(Concrete Factory):实现了抽象工厂接口,负责实际创建具体产品的对象。

2 实现

我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。

FactoryPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。
请添加图片描述

3 Demo代码

在这里插入图片描述
Shape

/**
 * 公共接口
 */
public interface Shape {
    void draw();
}

Circle

/**
 * 圆形,形状的实现类
 */
public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

Rectangle

/**
 * 矩形,形状的实现类
 */
public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

Square

/**
 * 方形,形状的实现类
 */
public class Square implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}

ShapeFactory

/**
 * 形状工厂类【根据不同的参数创建对应的实例】
 */
public class ShapeFactory {

    //使用 getShape 方法获取形状类型的对象
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

FactoryPatternDemo

/**
 *    总结:用来根据不同的参数创建对象
 */
public class FactoryPatternDemo {

    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        //获取 Circle 的对象,并调用它的 draw 方法
        Shape shape1 = shapeFactory.getShape("CIRCLE");
        //调用 Circle 的 draw 方法
        shape1.draw();

        //获取 Rectangle 的对象,并调用它的 draw 方法
        Shape shape2 = shapeFactory.getShape("RECTANGLE");
        //调用 Rectangle 的 draw 方法
        shape2.draw();

        //获取 Square 的对象,并调用它的 draw 方法
        Shape shape3 = shapeFactory.getShape("SQUARE");
        //调用 Square 的 draw 方法
        shape3.draw();
    }
}

效果
在这里插入图片描述

4 开发案例

4.1 算法执行服务里,不同的任务数据来源不同,执行脚本类型不同,结果处理不同,使用工厂模式

请添加图片描述
说明:

  • TaskAbstractFactory命名成抽象工厂,可以创建出来DataSetHandle工厂,ScriptExecuteHandle工厂,ResultHandle工厂,但是实现时,时间关系,简化成简单工厂模式。
  • 工厂返回的对象,每次都是新创建出来的,因为这些handle每次创建初始化的参数是不同的,和下面的第二个案例有所不同

在这里插入图片描述

工厂类

/**
 * 任务抽象工厂类,创建各种处理类
 * @since 2023 -10-08 16:13
 */
public class TaskAbstractFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(TaskAbstractFactory.class);

    /**
     * Gets script execute handle.
     *
     * @param scriptParam the script param
     * @param algoId the algo id
     * @return the script execute handle
     */
    public static ScriptExecuteHandle getScriptExecuteHandle(ScriptParam scriptParam, String algoId) {
        if (Constants.ScriptType.PYTHON.equals(scriptParam.getScriptType()) || Constants.ScriptType.PYTHON3.equals(
            scriptParam.getScriptType())) {
            return new PythonScriptExecuteHandle(scriptParam);
        } else {
            LOGGER.error("The algorithm type is not supported. algoId: {} ,scriptType: {} ", algoId,
                scriptParam.getScriptType());
            throw new CommonServiceException(AIModelError.ALGO_TYPE_NOT_SUPPORTED);
        }
    }

    /**
     * Gets data set handle list.
     *
     * @param dataSets the data sets
     * @return the data set handle list
     */
    public static List<DataSetHandle> getDataSetHandleList(List<InputDataSource> dataSets) {
        ArrayList<DataSetHandle> dataSetHandleList = new ArrayList<>(10);
        for (InputDataSource inputDataSource : dataSets) {
            dataSetHandleList.add(getDataSetHandle(inputDataSource));
        }
        return dataSetHandleList;
    }

    /**
     * Gets data set handle.
     *
     * @param inputDataSource the input data source
     * @return the data set handle
     */
    public static DataSetHandle getDataSetHandle(InputDataSource inputDataSource) {
        if (Constants.DataSourceType.MINIO_FILE.equalsIgnoreCase(inputDataSource.getDatatype())) {
            DataSetHandle dataSetHandle = new S3DataSetHandle(inputDataSource.getDataSourceInfo(),
                inputDataSource.getDataSourceConfig());
            // 进行数据源校验
            dataSetHandle.checkDataSource();
            return dataSetHandle;
        } else if (Constants.DataSourceType.HDFS_FILE.equalsIgnoreCase(inputDataSource.getDatatype())) {
            DataSetHandle dataSetHandle = new HdfsDataSetHandle(inputDataSource.getDataSourceInfo(),
                inputDataSource.getDataSourceConfig());
            // 进行数据源校验
            dataSetHandle.checkDataSource();
            return dataSetHandle;
        } else {
            LOGGER.error("The data source type is not supported. datatype: {} ", inputDataSource.getDatatype());
            throw new CommonServiceException(AIModelError.DATA_TYPE_NOT_SUPPORTED);
        }
    }

    /**
     * Gets result handle.
     *
     * @param calculateParam the calculate param
     * @param scriptParam the script param
     * @return the result handle
     */
    public static ResultHandle getResultHandle(CalculateParam calculateParam, ScriptParam scriptParam) {
        // 【预留】 设置结果集存放的数据源。目前所有的算法都有数据集,取数据集的第一个数据源作为结果上传的数据源
        OutputDataSource outputDataSource = getOutDataSource(calculateParam.getDataSets().get(0));

        if (Constants.CustomizedAlgoId.POTENTIAL_GUEST_ALGO_ID.equals(calculateParam.getAlgoId())) {
            // 定制化的处理方式,需要走特有的处理方式。此类算法,只能提前预置后
            return new PotentialGuestResultHandle(scriptParam, outputDataSource);
        } else {
            // 任务结果走默认处理方式,此类算法可以通过算法管理界面可以添加
            return new ResultHandle(scriptParam, outputDataSource);
        }
    }

    private static OutputDataSource getOutDataSource(InputDataSource inputDataSource) {
        return new OutputDataSource(inputDataSource.getDatatype(), inputDataSource.getDataSourceConfig());
    }

}

使用

    public TaskEntity(TaskInfo taskInfo, CalculateParam calculateParam, String algoPackDir, String localFileDir) {
        this.scriptParam = getScriptParam(calculateParam, algoPackDir, localFileDir);
        this.taskInfo = taskInfo;
        this.shellParamHandle = TaskAbstractFactory.getScriptExecuteHandle(scriptParam, calculateParam.getAlgoId());
        this.dataSetHandleList = TaskAbstractFactory.getDataSetHandleList(calculateParam.getDataSets());
        this.resultHandle = TaskAbstractFactory.getResultHandle(calculateParam, scriptParam);
    }

其他说明
设计是工厂创建实例的是子类,返回的是父类。多态的体现。同事父类有默认方法,子类是对父类的扩展, 或者重写。如下:
DataSetHandle

/**
 * 数据集处理父类
 * @since 2023 -09-15 15:35
 */
public abstract class DataSetHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataSetHandle.class);

    /**
     * The Data source info.
     */
    protected String dataSourceInfo;

    /**
     * The Data source config.
     */
    protected String dataSourceConfig;

    /**
     * Instantiates a new Data set handle.
     *
     * @param dataSourceInfo the data source info
     * @param dataSourceConfig the data source config
     */
    public DataSetHandle(String dataSourceInfo, String dataSourceConfig) {
        this.dataSourceInfo = dataSourceInfo;
        this.dataSourceConfig = dataSourceConfig;
    }

    /**
     * Check data source.
     */
    public void checkDataSource() {
        // 对参数的json格式进行校验
        if (!MyStringUtil.checkJson(dataSourceInfo)) {
            LOGGER.error("dataSourceInfo json format error.");
            throw new CommonServiceException(AIModelError.PARAM_ERROR, "dataSourceInfo json format error.");
        }

        if (StringUtils.isNotEmpty(dataSourceConfig) && !MyStringUtil.checkJson(dataSourceConfig)) {
            LOGGER.error("dataSourceConfig json format error.");
            throw new CommonServiceException(AIModelError.PARAM_ERROR, "dataSourceConfig json format error.");
        }
    }

    /**
     * Handle data.
     *
     * @param taskId the task id
     * @param localFileDir the local file dir
     */
    public abstract void handleData(String taskId, String localFileDir);

}

S3DataSetHandle

/**
 * S3或者minio类型的数据集处理类
 * @since 2023 -09-15 15:35
 */
public class S3DataSetHandle extends DataSetHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(S3DataSetHandle.class);

    /**
     * Instantiates a new S 3 data set handle.
     *
     * @param dataSourceInfo the data source info
     * @param dataSourceConfig the data source config
     */
    public S3DataSetHandle(String dataSourceInfo, String dataSourceConfig) {
        super(dataSourceInfo, dataSourceConfig);
    }

    /**
     * Check data source.
     */
    @Override
    public void checkDataSource() {
        // 1 父类进行参数json格式的校验
        super.checkDataSource();
        // 2 具体子类,进行特性校验
        List<S3DataSourceInfo> s3DataSourceList = JSON.parseArray(dataSourceInfo, S3DataSourceInfo.class);
        for (S3DataSourceInfo s3DataSource : s3DataSourceList) {
            // 目前S3,仅支持zip文件
            if (!Constants.FileType.ZIP.equalsIgnoreCase(s3DataSource.getFileType())) {
                LOGGER.error("The file type is not supported. fileType:{}", s3DataSource.getFileType());
                throw new CommonServiceException(AIModelError.PARAM_ERROR,
                    "The file type is not supported. fileType: " + s3DataSource.getFileType());
            }
            if (StringUtils.isEmpty(s3DataSource.getFileId()) || StringUtils.isEmpty(s3DataSource.getStoreDirName())) {
                LOGGER.error("fileId and storeDirName cannot be empty.");
                throw new CommonServiceException(AIModelError.PARAM_ERROR, "fileId and storeDirName cannot be empty.");
            }
        }
    }

    /**
     * Handle data.
     *
     * @param taskId the task id
     * @param localFileDir the local file dir
     */
    @Override
    public void handleData(String taskId, String localFileDir) {
        // 1 获取配置
        S3DataSourceConfig s3DataSourceConfig = JSON.parseObject(dataSourceConfig, S3DataSourceConfig.class);
        // 2 初始化S3客户端
        S3ClientUtils s3ClientUtils = S3ClientUtils.getInstance(s3DataSourceConfig);
        for (S3DataSourceInfo s3DataSourceInfo : JSON.parseArray(dataSourceInfo, S3DataSourceInfo.class)) {
            InputStream s3InputStream = null;
            try {
                // 3 获取数据流
                s3InputStream = s3ClientUtils.download(s3DataSourceConfig.getBucketName(),
                    s3DataSourceInfo.getFileId());
                // 4 将文件保存在本地磁盘
                saveFileToLocal(s3InputStream, s3DataSourceInfo, taskId, localFileDir + taskId);
            } finally {
                MyIOUtils.closeInputStream(s3InputStream);
            }
        }
    }
}

4.2 网络还原时,不同的采集数据,解析处理方式不同

说明:

  • 创建对象由spring类管理,创建出来的对象是单例的,这个案例1有所不同,案例1,每个类初始化的参数不同。这个案例对象初始化方式一样,只是处理逻辑不同。
  • 在vimpim对象是,因为参数不同所有每次都需要new

工厂类:

/**
 * The type Ods process factory.
 *
 * @since 2024 -06-11 10:54
 */
@Slf4j
@Service
public class OdsProcessFactory {

    private static final List<VimPimModelDto> VIM_PIM_MODEL_V2_LIST = new ArrayList<>();
    private static final String VIM_PIM_MODEL_V2_FILE_PATH = "2.json";
    private static final List<String> VIM_PIM_MODEL_V2_WHITE_LIST = Arrays.asList();
    private static final List<VimPimModelDto> VIM_PIM_MODEL_V3_LIST = new ArrayList<>();
    private static final String VIM_PIM_MODEL_V3_FILE_PATH = "3.json";
    private static final List<String> VIM_PIM_MODEL_V3_WHITE_LIST = Arrays.asList();

    /**
     * The Data source.
     */
    @Resource(name = "gauss")
    DataSource dataSource;

    @Autowired
    private VimPimRepository vimPimRepository;

    @Autowired
    private VnfMaeCnDataProcessDomainService vnfMaeCnDataProcessDomainService;

    @Autowired
    private VnflocLcmDataProcessDomainService vnflocLcmDataProcessDomainService;

    static {
        // 初始化模型数据
        initModel(VIM_PIM_MODEL_V2_FILE_PATH, VIM_PIM_MODEL_V2_WHITE_LIST, VIM_PIM_MODEL_V2_LIST);
        initModel(VIM_PIM_MODEL_V3_FILE_PATH, VIM_PIM_MODEL_V3_WHITE_LIST, VIM_PIM_MODEL_V3_LIST);
    }

    private static void initModel(String vimPimModelFilePath, List<String> vimPimModelWhiteList,
        List<VimPimModelDto> vimPimModelList) {
    }

    /**
     * Gets ods process service.
     *
     * @param taskDto the task dto
     * @param dataFormat the data format
     * @return the ods process service
     */
    public DataProcessDomainService getOdsProcessService(CollectionTaskDto taskDto, int dataFormat) {
        if (OdsParseConstants.OdsDataFormat.VNF_MAECN == dataFormat
            || OdsParseConstants.OdsDataFormat.NIC_DSP == dataFormat) {
            return vnfMaeCnDataProcessDomainService;
        } else if (OdsParseConstants.OdsDataFormat.VIM_OV2 == dataFormat
            || OdsParseConstants.OdsDataFormat.PIM_OV2 == dataFormat) {
            return getVimPimDataProcessV2DomainService();
        } else if (OdsParseConstants.OdsDataFormat.VIM_OV3 == dataFormat
            || OdsParseConstants.OdsDataFormat.PIM_OV3 == dataFormat) {
            return getVimPimDataProcessV3DomainService();
        } else if (OdsParseConstants.OdsDataFormat.VNFLOC_LCM == dataFormat) {
            return vnflocLcmDataProcessDomainService;
        } else {
            // 采集来的数据格式不支持
            log.warn("Incorrect data dataFormat. taskId:{}, tasksSn:{}, dataFormat:{} ", taskDto.getTaskId(),
                taskDto.getTaskSn(), dataFormat);
        }
        return null;
    }

    public VimPimDataProcessV2DomainService getVimPimDataProcessV2DomainService() {
        return new VimPimDataProcessV2DomainService(VIM_PIM_MODEL_V2_LIST, dataSource, vimPimRepository);
    }

    public VimPimDataProcessV3DomainService getVimPimDataProcessV3DomainService() {
        return new VimPimDataProcessV3DomainService(VIM_PIM_MODEL_V3_LIST, dataSource, vimPimRepository);
    }
}

在这里插入图片描述


http://www.kler.cn/news/361418.html

相关文章:

  • Zookeeper面试整理-源码及实现细节
  • 02_Linux基础配置指南
  • 算法Day-7
  • 企业级调度器 LVS
  • iOS Swift逆向——deMangle过程中的偏移计算
  • QMT和PTrade在交易品种支持上有何不同?
  • ES6面试题:(第一天)
  • nextjs项目中,使用postgres的完整案例
  • C#第5讲:类和对象的使用
  • 408算法题leetcode--第37天
  • 【经验】无线鼠标、键盘的usb接收器配对
  • autMan内置redis服务的使用方法
  • 记,项目解决
  • androidrro ResourceOverlay 调查
  • Gstreamer的webrtcbin插件
  • Nova-Admin:基于Vue3、Vite、TypeScript和NaiveUI的开源简洁灵活管理模板
  • Java代码说明设计模式
  • QT--文本框 QLineEdit、qtextedit
  • 深度学习——线性神经网络(五、图像分类数据集——Fashion-MNIST数据集)
  • C++ TVM stack 继续
  • GAMES104:17 游戏引擎的玩法系统:高级AI-学习笔记
  • 流量PID控制(开度前馈量计算+辅助PID)
  • 2024人工智能技术的普及 如何看待AI技术的应用前景
  • 文件(下)
  • 2024.10.22 软考学习笔记
  • 阅读笔记 Marketing Management Chapter 12