欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > 设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。

设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。

2025/4/6 8:31:59 来源:https://blog.csdn.net/qq_40853919/article/details/147005746  浏览:    关键词:设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。

目录

  1. 问题是什么,为什么使用模板方法
  2. 模板设计模式是什么
  3. 实际的应用场景

一、问题

// 咖啡制作
class CoffeeMaker {fun makeCoffee() {boilWater()println("Brewing coffee grounds")pourInCup()println("Adding sugar and milk")}private fun boilWater() = println("Boiling water")private fun pourInCup() = println("Pouring into cup")
}// 茶制作
class TeaMaker {fun makeTea() {boilWater()println("Steeping tea bag")pourInCup()println("Adding lemon")}private fun boilWater() = println("Boiling water")private fun pourInCup() = println("Pouring into cup")
}

问题分析:

❌ 重复代码:boilWater() 和 pourInCup() 重复

❌ 维护困难:修改流程需要改动所有实现类

这里,我们就需要引出我们今天的主角,模板设计模式。


二、模板设计模式是什么

在父类中定义流程步骤,允许子类在不改变原有步骤的情况下重写特定步骤,下面直接上代码。

// 抽象模板类
abstract class BeverageMaker {// 模板方法(final禁止子类覆盖)final fun prepareBeverage() {boilWater()brew()pourInCup()addCondiments()}// 具体步骤实现private fun boilWater() = println("Boiling water")private fun pourInCup() = println("Pouring into cup")// 抽象方法(必须由子类实现)abstract fun brew()abstract fun addCondiments()
}// 具体实现类
class CoffeeMaker : BeverageMaker() {override fun brew() = println("Brewing coffee grounds")override fun addCondiments() = println("Adding sugar and milk")
}class TeaMaker : BeverageMaker() {override fun brew() = println("Steeping tea bag")override fun addCondiments() = println("Adding lemon")
}// 使用示例
fun main() {val coffee = CoffeeMaker()coffee.prepareBeverage()println("\n---------------\n")val tea = TeaMaker()tea.prepareBeverage()
}

可以看到,我们只需要使用抽象父类,子类进行具体实现,就可以完成模板设计模式。

带来的好处就是,重复代码没了。

2.1 接下来我们看一个实际的应用场景

我们有一个图片文件上传功能和图片url上传功能,这个功能里面,大部分逻辑是一样的,只有特定的不一样,我们看看。

图片文件上传功能


public UploadPictureResult uploadPicture(MultipartFile multipartFile, String uploadPathPrefix) {//校验图片:大小validPicture(multipartFile);//图片上传地址//1. 业务上传图片区分   2. 随机数 3.原始文件名称。使用原始文件名称会存在安全性问题//2. 文件前缀String uuid = RandomUtil.randomNumbers(16);String originalFilename = multipartFile.getOriginalFilename();String uploadFilename = String.format("%s_%s_%s", DateUtil.formatDate(new Date()), uuid,originalFilename);String uploadPath = String.format("/%s/%s",uploadPathPrefix,uploadFilename);//然后发送给对象存储File tempFile =null;try {//解析结果并返回tempFile = File.createTempFile(uploadPath, null);multipartFile.transferTo(tempFile);PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, tempFile);ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();int width = imageInfo.getWidth();int height = imageInfo.getHeight();double picScale = NumberUtil.round(width *1.0 /height,2).doubleValue();UploadPictureResult uploadPictureResult = new UploadPictureResult();uploadPictureResult.setUrl(cosClientConfig.getHost()+"/"+uploadPath);uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));uploadPictureResult.setPicSize(FileUtil.size(tempFile));uploadPictureResult.setPicWidth(width);uploadPictureResult.setPicHeight(height);uploadPictureResult.setPicScale(picScale);uploadPictureResult.setPicFormat(imageInfo.getFormat());return uploadPictureResult;} catch (Exception e) {log.error("file upload error,filepath = "+ uploadPath,e);throw new BusinessException(ErrorCode.SYSTEM_ERROR,"上传失败");}finally {//临时文件清理deleteTempFile(tempFile);}}

图片url上传功能

public UploadPictureResult uploadPictureByUrl(String fileUrl, String uploadPathPrefix) {//校验图片:大小//validPicture(multipartFile);//todovalidPicture(fileUrl);//图片上传地址//1. 业务上传图片区分   2. 随机数 3.原始文件名称。使用原始文件名称会存在安全性问题//2. 文件前缀String uuid = RandomUtil.randomNumbers(16);//todo 获取图片原始名字//String originalFilename = multipartFile.getOriginalFilename();String originalFilename = FileUtil.mainName(fileUrl);String uploadFilename = String.format("%s_%s_%s", DateUtil.formatDate(new Date()), uuid,originalFilename);String uploadPath = String.format("/%s/%s",uploadPathPrefix,uploadFilename);//然后发送给对象存储File tempFile =null;try {//解析结果并返回tempFile = File.createTempFile(uploadPath, null);//todo 下载文件//multipartFile.transferTo(tempFile);HttpUtil.downloadFile(fileUrl,tempFile);PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, tempFile);ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();int width = imageInfo.getWidth();int height = imageInfo.getHeight();double picScale = NumberUtil.round(width *1.0 /height,2).doubleValue();UploadPictureResult uploadPictureResult = new UploadPictureResult();uploadPictureResult.setUrl(cosClientConfig.getHost()+"/"+uploadPath);uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));uploadPictureResult.setPicSize(FileUtil.size(tempFile));uploadPictureResult.setPicWidth(width);uploadPictureResult.setPicHeight(height);uploadPictureResult.setPicScale(picScale);uploadPictureResult.setPicFormat(imageInfo.getFormat());return uploadPictureResult;} catch (Exception e) {log.error("file upload error,filepath = "+ uploadPath,e);throw new BusinessException(ErrorCode.SYSTEM_ERROR,"上传失败");}finally {//临时文件清理deleteTempFile(tempFile);}
}

这两份代码里面,只有三个地方不一样:

  1. validPicture(multipartFile);
  2. String originalFilename = multipartFile.getOriginalFilename();
  3. multipartFile.transferTo(tempFile);

其他的都是一样,我们可以使用模板方法进行抽象,但是这里有个问题就是,有个参数是String,有个参数是MultipartFile,类型不一样,怎么办呢?我们可以使用泛型或者Object来解决,这里我们就使用Object。


2.1.1 抽象一个父类

这个父类里面,有一个公共方法uploadPicture,参数为object,有三个抽象方法:validPicture、getOriginFilename以及processFile。

@Slf4j
public abstract class PictureUploadTemplate {  @Resourceprotected CosManager cosManager;@Resource  protected CosClientConfig cosClientConfig;/**  * 模板方法,定义上传流程  */  public final UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix) {// 1. 校验图片  validPicture(inputSource);  // 2. 图片上传地址  String uuid = RandomUtil.randomString(16);String originFilename = getOriginFilename(inputSource);  String uploadFilename = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid,FileUtil.getSuffix(originFilename));String uploadPath = String.format("/%s/%s", uploadPathPrefix, uploadFilename);  File file = null;  try {  // 3. 创建临时文件  file = File.createTempFile(uploadPath, null);// 处理文件来源(本地或 URL)  processFile(inputSource, file);  // 4. 上传图片到对象存储  PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();// 5. 封装返回结果  return buildResult(originFilename, file, uploadPath, imageInfo);  } catch (Exception e) {  log.error("图片上传到对象存储失败", e);  throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");} finally {  // 6. 清理临时文件  deleteTempFile(file);  }  }  /**  * 校验输入源(本地文件或 URL)  */  protected abstract void validPicture(Object inputSource);  /**  * 获取输入源的原始文件名  */  protected abstract String getOriginFilename(Object inputSource);  /**  * 处理输入源并生成本地临时文件  */  protected abstract void processFile(Object inputSource, File file) throws Exception;  /**  * 封装返回结果  */  private UploadPictureResult buildResult(String originFilename, File file, String uploadPath, ImageInfo imageInfo) {  UploadPictureResult uploadPictureResult = new UploadPictureResult();  int picWidth = imageInfo.getWidth();  int picHeight = imageInfo.getHeight();  double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();uploadPictureResult.setPicName(FileUtil.mainName(originFilename));  uploadPictureResult.setPicWidth(picWidth);  uploadPictureResult.setPicHeight(picHeight);  uploadPictureResult.setPicScale(picScale);  uploadPictureResult.setPicFormat(imageInfo.getFormat());  uploadPictureResult.setPicSize(FileUtil.size(file));  uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + uploadPath);  return uploadPictureResult;  }  /**  * 删除临时文件  */  public void deleteTempFile(File file) {  if (file == null) {  return;  }  boolean deleteResult = file.delete();  if (!deleteResult) {  log.error("file delete error, filepath = {}", file.getAbsolutePath());  }  }  
}

2.1.2 子类实现


@Service
public class UrlPictureUpload extends PictureUploadTemplate {  @Override  protected void validPicture(Object inputSource) {  String fileUrl = (String) inputSource;  ThrowUtils.throwIf(StrUtil.isBlank(fileUrl), ErrorCode.PARAMS_ERROR, "文件地址不能为空");// ... 跟之前的校验逻辑保持一致  }  @Override  protected String getOriginFilename(Object inputSource) {  String fileUrl = (String) inputSource;  // 从 URL 中提取文件名  return FileUtil.mainName(fileUrl);}  @Override  protected void processFile(Object inputSource, File file) throws Exception {String fileUrl = (String) inputSource;  // 下载文件到临时目录  HttpUtil.downloadFile(fileUrl, file);}  
}

@Service
public class FilePictureUpload extends PictureUploadTemplate {  @Override  protected void validPicture(Object inputSource) {  MultipartFile multipartFile = (MultipartFile) inputSource;ThrowUtils.throwIf(multipartFile == null, ErrorCode.PARAMS_ERROR, "文件不能为空");// 1. 校验文件大小  long fileSize = multipartFile.getSize();  final long ONE_M = 1024 * 1024L;  ThrowUtils.throwIf(fileSize > 2 * ONE_M, ErrorCode.PARAMS_ERROR, "文件大小不能超过 2M");  // 2. 校验文件后缀  String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());// 允许上传的文件后缀  final List<String> ALLOW_FORMAT_LIST = Arrays.asList("jpeg", "jpg", "png", "webp");ThrowUtils.throwIf(!ALLOW_FORMAT_LIST.contains(fileSuffix), ErrorCode.PARAMS_ERROR, "文件类型错误");  }  @Override  protected String getOriginFilename(Object inputSource) {  MultipartFile multipartFile = (MultipartFile) inputSource;  return multipartFile.getOriginalFilename();  }  @Override  protected void processFile(Object inputSource, File file) throws Exception {MultipartFile multipartFile = (MultipartFile) inputSource;  multipartFile.transferTo(file);  }  
}

2.1.3 进行使用


@Resource
private FilePictureUpload filePictureUpload;@Resource
private UrlPictureUpload urlPictureUpload;@Override
public PictureVO uploadPicture(Object inputSource, PictureUploadRequest pictureUploadRequest, User loginUser) {PictureUploadTemplate pictureUploadTemplate = filePictureUpload;if (inputSource instanceof String){pictureUploadTemplate = urlPictureUpload;}UploadPictureResult uploadPictureResult = pictureUploadTemplate.uploadPicture(inputSource, uploadPathPrefix);}

通过判断inputSource不同的类型来使用不同的模板子类

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词