现在有一个文件处理的方法,里面需要判断文档的各种类型、比如img、docx等,然后不同的类型有不同的处理逻辑,大致代码结构如下:
if (fileName.endsWith(".img")) {
// todo
exec();
} else if (fileName.endsWith(".docx")) {
// todo
exec();
} else if (fileName.endsWith(".txt")) {
// todo
exec();
} else {
// todo
exec();
}
现在有了新的需求,每种类型的文件处理前需要判断文档大小,根据大小做出一些操作,我确实可以在if里面嵌套判断去做,但是考虑到以后的扩展性,以及多人开发时代码的越来越不整洁,打算使用设计模式重构下这部分的代码,以达到规范开发的效果。
经过思考,可以选择策略+map
或者责任链
模式实现,经过考虑,最终选择了责任链来处理。
很好理解,我的需求是符合条件,则处理,不符合则向下滚动,直到有合适的处理者可以处理。
而在责任链模式里,请求自上而下沿链传递,直到链上的某一个对象决定处理不谋而合。
那开始上代码。
public interface IFileInfoHandleChain {
/**
* 责任链接口
* 大文件、image、docx等
*/
void handleFileInfo(FileInfo fileInfo) throws Exception;
}
public interface IFileInfoHandler {
/**
* 执行真正的逻辑
*/
void handlerFileInfo(FileInfo fileInfo, IFileInfoHandleChain chain) throws Exception;
}
其实抽象处理类可以按需实现,这里只是整体封装冗余操作,比如一般的责任链节点处理方式:
@Override
public void handlerFileInfo(FileInfo fileInfo, IFileInfoHandleChain chain) throws Exception {
// 满足条件则处理,处理完成后责任链结束
if (true) {
// todo
}
//处理不了往下传递
else {
chain.handlerDocumentInfo(receipt);
}
}
很明显这个else块儿内是冗余操作,而且也缺少异常处理,如果当前处理节点异常,则应该向下传递。
创建一个AbstractIFileInfoHandler
类,新增两个抽象方法condition(FileInfo fileInfo)
,handler(FileInfo fileInfo)
。
处理者需要继承并实现这两个方法。
public abstract class AbstractIFileInfoHandler implements IFileInfoHandler {
protected Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 触发条件
*/
protected abstract Boolean condition(FileInfo fileInfo);
/**
* 触发条件后执行的代码
*/
public abstract void handler(FileInfo fileInfo) throws Exception;
@Override
public void handlerFileInfo(FileInfo fileInfo, IFileInfoHandleChain chain) throws Exception {
if (condition(fileInfo)) {
try {
handler(fileInfo);
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
chain.handleFileInfo(fileInfo);
}
} else {
chain.handleFileInfo(fileInfo);
}
}
}
现在我们可以通过继承的方式来创建真正的节点类,比如创建一个DocxFileInfoHandler
类:
@Component
public class DocxFileInfoHandler extends AbstractIFileInfoHandler {
@Override
protected Boolean condition(FileInfo fileInfo) {
return fileInfo.getName.endsWith("docx");
}
@Override
public void handler(FileInfo fileInfo) throws Exception {
// todo
}
}
那么当有多个节点类时,如何控制执行顺序?创建一个注解,只需要在节点类上面添加即可,value越小越靠前处理:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ChainOrder {
/**
* 责任链顺序
*/
int value() default Integer.MAX_VALUE;
}
使用方式如下:
@Component
@ChainOrder(5)
public class ImageFileInfoHandler extends AbstractIFileInfoHandler {
@Override
protected Boolean condition(FileInfo fileInfo) {
return null;
}
@Override
public void handler(FileInfo fileInfo) throws Exception {
}
}
如果是非spring的项目,直接new即可,这里演示的是spring项目,需要使用容器提供的bean。
创建FileInfoHandlerContainer
,该类提供所有的处理者
@Component
@Slf4j
public class FileInfoHandlerContainer {
private FileInfoHandlerContainer() {
}
private static ApplicationContext context;
@Autowired
public void setApplicationContext(ApplicationContext context) {
FileInfoHandlerContainer.context = context;
}
private static List<IFileInfoHandler> documentTransHandlers = null;
public static List<IFileInfoHandler> getSingleFileInfoHandlers() {
if (documentTransHandlers == null) {
documentTransHandlers = new ArrayList<>();
Map<IFileInfoHandler, Integer> handlerOrderMap = new HashMap<>();
final Collection<IFileInfoHandler> handlers = context.getBeansOfType(IFileInfoHandler.class).values();
for (IFileInfoHandler handler : handlers) {
for (Annotation annotation : handler.getClass().getAnnotations()) {
if (annotation.annotationType().equals(ChainOrder.class)){
final ChainOrder order = (ChainOrder) annotation;
handlerOrderMap.put(handler, order.value());
}
}
handlerOrderMap.putIfAbsent(handler, 999);
}
List<Map.Entry<IFileInfoHandler, Integer>> sortedHandlers = handlerOrderMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toList());
for (Map.Entry<IFileInfoHandler, Integer> entry : sortedHandlers) {
documentTransHandlers.add(entry.getKey());
}
}
return documentTransHandlers;
}
}
public class IFileInfoHandleChainImpl implements IFileInfoHandleChain {
//当前处理者位置
private int index = 0;
@Override
public void handleFileInfo(FileInfo fileInfo) throws Exception{
final List<IFileInfoHandler> handlers = FileInfoHandlerContainer.getSingleFileInfoHandlers();
if (handlers != null && handlers.size() > 0) {
if (index != handlers.size()) {
IFileInfoHandler documentTransHandler = handlers.get(index++);
documentTransHandler.handlerFileInfo(fileInfo, this);
}
}
}
}
public class FileInfoHandleChainFactory {
public static IFileInfoHandleChain create() {
return new IFileInfoHandleChainImpl();
}
}
经过对比,很好的消除了大量的if-else,并且有新需求的时候新增处理节点即可,看起来更优雅了!
public static void main(String[] args) {
final IFileInfoHandleChain chain = FileInfoHandleChainFactory.create();
try {
chain.handleFileInfo(new FileInfo());
} catch (Exception e) {
e.printStackTrace();
}
}