-
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,它提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离。
-
工厂模式提供了一种创建对象的方式,而无需指定要创建的具体类。
-
通过使用工厂模式,可以将对象的创建逻辑封装在一个工厂类中,而不是在客户端代码中直接实例化对象,这样可以提高代码的可维护性和可扩展性。
设计模式,最近持续更新中,如需要请关注
如果你觉得我分享的内容或者我的努力对你有帮助,或者你只是想表达对我的支持和鼓励,请考虑给我点赞、评论、收藏。您的鼓励是我前进的动力,让我感到非常感激。
文章目录
- 1 概要
- 2 实现
- 3 Demo代码
- 4 开发案例
- 4.1 算法执行服务里,不同的任务数据来源不同,执行脚本类型不同,结果处理不同,使用工厂模式
- 4.2 网络还原时,不同的采集数据,解析处理方式不同
1 概要
意图
定义一个创建对象的接口,让其子类决定实例化哪一个具体的类。工厂模式使对象的创建过程延迟到子类。
主要解决
接口选择的问题。
何时使用
当我们需要在不同条件下创建不同实例时。
如何解决
通过让子类实现工厂接口,返回一个抽象的产品。
关键代码
对象的创建过程在子类中实现。
应用实例
- 汽车制造:你需要一辆汽车,只需从工厂提货,而不需要关心汽车的制造过程及其内部实现。
- Hibernate:更换数据库时,只需更改方言(Dialect)和数据库驱动(Driver),即可实现对不同数据库的切换。
优点
- 调用者只需要知道对象的名称即可创建对象。
- 扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
- 屏蔽了产品的具体实现,调用者只关心产品的接口。
缺点
每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。
使用场景
- 日志记录:日志可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志的位置。
- 数据库访问:当用户不知道最终系统使用哪种数据库,或者数据库可能变化时。
- 连接服务器的框架设计:需要支持 “POP3”、“IMAP”、“HTTP” 三种协议,可以将这三种协议作为产品类,共同实现一个接口。
- 在算法执行服务中,每个任务需要处理的数据来源不同,根据数据类型创建对应的数据出来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);
}
}