10.1. 模板方法模式
模板方法模式(Template Method Pattern)定义一个固定的算法框架,而将算法的一些步骤放到子类中实现,使得子类可以在不改变算法框架的情况下重定义该算法的某些步骤。
普通:
@Repository
public class UserValue {
/** 值操作 */
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> valueOperations;
/** 值模式 */
private static final String KEY_FORMAT = "Value:User:%s";
/** 设置值 */
public void set(Long id, UserDO value) {
String key = String.format(KEY_FORMAT, id);
valueOperations.set(key, JSON.toJSONString(value));
}
/** 获取值 */
public UserDO get(Long id) {
String key = String.format(KEY_FORMAT, id);
String value = valueOperations.get(key);
return JSON.parseObject(value, UserDO.class);
}
...
}
@Repository
public class RoleValue {
/** 值操作 */
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> valueOperations;
/** 值模式 */
private static final String KEY_FORMAT = "Value:Role:%s";
/** 设置值 */ public void set(Long id, RoleDO value) {
String key = String.format(KEY_FORMAT, id);
valueOperations.set(key, JSON.toJSONString(value));
}
/** 获取值 */ public RoleDO get(Long id) {
String key = String.format(KEY_FORMAT, id);
String value = valueOperations.get(key);
return JSON.parseObject(value, RoleDO.class);
}
...
}
精简:
public abstract class AbstractDynamicValue<I, V> {
/** 值操作 */
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> valueOperations;
/** 设置值 */ public void set(I id, V value) {
valueOperations.set(getKey(id), JSON.toJSONString(value));
}
/** 获取值 */
public V get(I id) {
return JSON.parseObject(valueOperations.get(getKey(id)), getValueClass());
}
/** 获取主键 */
protected abstract String getKey(I id);
/** 获取值类 */
protected abstract Class<V> getValueClass();
}
@Repository
public class UserValue extends AbstractValue<Long, UserDO> {
/** 获取主键 */
@Override
protected String getKey(Long id) {
return String.format("Value:User:%s", id);
}
/** 获取值类 */
@Override
protected Class<UserDO> getValueClass() {
return UserDO.class;
}
}
@Repository
public class RoleValue extends AbstractValue<Long, RoleDO> {
/** 获取主键 */
@Override
protected String getKey(Long id) {
return String.format("Value:Role:%s", id);
}
/** 获取值类 */
@Override protected Class<RoleDO> getValueClass() {
return RoleDO.class;
}
}
10.2. 建造者模式
建造者模式(Builder Pattern)将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。
普通:
public interface DataHandler<T> {
/** 解析数据 */
public T parseData(Record record);
/** 存储数据 */
public boolean storeData(List<T> dataList);}
public <T> long executeFetch(String tableName, int batchSize, DataHandler<T> dataHandler) throws Exception {
// 构建下载会话
DownloadSession session = buildSession(tableName);
// 获取数据数量
long recordCount = session.getRecordCount();
if (recordCount == 0) {
return 0;
}
// 进行数据读取
long fetchCount = 0L;
try (RecordReader reader = session.openRecordReader(0L, recordCount, true)) {
// 依次读取数据
Record record;
List<T> dataList = new ArrayList<>(batchSize);
while ((record = reader.read()) != null) {
// 解析添加数据
T data = dataHandler.parseData(record);
if (Objects.nonNull(data)) {
dataList.add(data);
}
// 批量存储数据
if (dataList.size() == batchSize) {
boolean isContinue = dataHandler.storeData(dataList);
fetchCount += batchSize;
dataList.clear();
if (!isContinue) {
break;
}
}
}
// 存储剩余数据
if (CollectionUtils.isNotEmpty(dataList)) {
dataHandler.storeData(dataList);
fetchCount += dataList.size();
dataList.clear();
}
}
// 返回获取数量
return fetchCount;
}
// 使用案例
long fetchCount = odpsService.executeFetch("user", 5000, new DataHandler() {
/** 解析数据 */
public T parseData(Record record) {
UserDO user = new UserDO();
user.setId(record.getBigint("id"));
user.setName(record.getString("name"));
return user;
}
/** 存储数据 */
boolean storeData(List<T> dataList) {
userDAO.batchInsert(dataList);
return true;
}
});
精简:
public <T> long executeFetch(String tableName, int batchSize, Function<Record, T> dataParser, Function<List<T>, Boolean> dataStorage) throws Exception {
// 构建下载会话
DownloadSession session = buildSession(tableName);
// 获取数据数量
long recordCount = session.getRecordCount();
if (recordCount == 0) {
return 0;
}
// 进行数据读取
long fetchCount = 0L;
try (RecordReader reader = session.openRecordReader(0L, recordCount, true)) {
// 依次读取数据
Record record;
List<T> dataList = new ArrayList<>(batchSize);
while ((record = reader.read()) != null) {
// 解析添加数据
T data = dataParser.apply(record);
if (Objects.nonNull(data)) {
dataList.add(data);
}
// 批量存储数据
if (dataList.size() == batchSize) {
Boolean isContinue = dataStorage.apply(dataList);
fetchCount += batchSize;
dataList.clear();
if (!Boolean.TRUE.equals(isContinue)) {
break;
}
}
}
// 存储剩余数据
if (CollectionUtils.isNotEmpty(dataList)) {
dataStorage.apply(dataList);
fetchCount += dataList.size();
dataList.clear();
}
}
// 返回获取数量
return fetchCount;
}
// 使用案例
long fetchCount = odpsService.executeFetch("user", 5000, record -> {
UserDO user = new UserDO();
user.setId(record.getBigint("id"));
user.setName(record.getString("name"));
return user;
}, dataList -> {
userDAO.batchInsert(dataList);
return true;
});
普通的建造者模式,实现时需要定义 DataHandler 接口,调用时需要实现 DataHandler 匿名内部类,代码较多较繁琐。而精简后的建造者模式,充分利用了函数式编程,实现时无需定义接口,直接使用 Function 接口;调用时无需实现匿名内部类,直接采用 lambda 表达式,代码较少较简洁。
10.3. 代理模式
Spring 中最重要的代理模式就是 AOP (Aspect-Oriented Programming,面向切面的编程),是使用 JDK 动态代理和 CGLIB 动态代理技术来实现的。
普通:
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
/** 用户服务 */
@Autowired
private UserService userService;
/** 查询用户 */
@PostMapping("/queryUser")
public Result<?> queryUser(@RequestBody @Valid UserQueryVO query) {
try {
PageDataVO<UserVO> pageData = userService.queryUser(query);
return Result.success(pageData);
} catch (Exception e) {
log.error(e.getMessage(), e);
return Result.failure(e.getMessage());
}
}
...
}
精简 1:
基于 @ControllerAdvice 的异常处理:
@RestController
@RequestMapping("/user")
public class UserController {
/** 用户服务 */
@Autowired
private UserService userService;
/** 查询用户 */
@PostMapping("/queryUser")
public Result<PageDataVO<UserVO>> queryUser(@RequestBody @Valid UserQueryVO query) {
PageDataVO<UserVO> pageData = userService.queryUser(query);
return Result.success(pageData);
}
...
}
@Slf4j
@ControllerAdvice
public class GlobalControllerAdvice {
/** 处理异常 */
@ResponseBody
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error(e.getMessage(), e);
return Result.failure(e.getMessage());
}
}
精简 2:
基于 AOP 的异常处理:
// UserController 代码同 "精简 1"
@Slf4j
@Aspect
public class WebExceptionAspect {
/** 点切面 */
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void webPointcut() {}
/** 处理异常 */
@AfterThrowing(pointcut = "webPointcut()", throwing = "e")
public void handleException(Exception e) {
Result<Void> result = Result.failure(e.getMessage());
writeContent(JSON.toJSONString(result));
}
}
正文完