优化前

原来的代码大概长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public String generate(String templateId, List<String> componentList, List<Path> uploadFileList ......)
{
if (StringUtils.isNotBlank(templateId))
{
// service a process template...
}

if (CollectionUtils.isNotEmpty(componentList))
{
// service b process components...
}

if (CollectionUtils.isNotEmpty(uploadFileList))
{
// service c process upload files...
}

// 省略其他处理逻辑

if (CollectionUtils.isNotEmpty(uploadFileList))
{
return "some result A";
}
return "some result B";
}

总而言之,就是根据传入的参数,分别做某些事情。如果需求改变了,需要新增一些功能,则一方面增加方法入参,一方面在合适的代码位置加入新逻辑。
这样下去,方法变得越来越大,维护起来比较困难。因此决定优化以下。

经过考虑,我决定采用行为模式中的责任链模式来优化。以下是优化后的代码的大概样子。

优化后

首先,定义一个上下文类,该类保存所有service需要处理的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GeneratorContext
{

private String templateId;

private String targetFileDirectory;

private List<Path> localFilePaths = Lists.newArrayList();

private List<String> componentList = Lists.newArrayList();

private List<Path> uploadFileList = Lists.newArrayList();

private String generateResult;
}

注意,这里有一个问题,就是在一开始的代码中,如果有上传文件,返回结果A,否则返回结果B(一个比喻,实际代码可能不是这样)。在采用责任链模式改写的过程中,一直无法很好地获取个个责任者返回的值。因此,在上下文信息类中保存了generateResult,表示返回值,由各个责任者指定值。

接下来定义抽象的责任类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractGenerator
{

private AbstractGenerator nextGenerator;

public AbstractGenerator setNextGenerator(AbstractGenerator nextGenerator)
{
this.nextGenerator = nextGenerator;
return this.nextGenerator;
}

public void generateProject(GeneratorContext context)
{
if (needMyHelp(context))
{
generateMyPart(context);
}
if (this.nextGenerator != null)
{
this.nextGenerator.generateProject(context);
}
}

protected abstract void generateMyPart(GeneratorContext context);

protected abstract boolean needMyHelp(GeneratorContext context);
}

然后是该各个具体的责任者,继承自该抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Component("baseGenerator")
public class BaseGenerator extends AbstractGenerator
{

@Override
protected void generateMyPart(GeneratorContext context)
{
try
{
for (Path localFile : context.getLocalFilePaths())
{
Path targetFilePath = Paths.get(context.getTargetFileDirectory()).resolve(localFile.getFileName());
Files.copy(localFile, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
}
context.setGenerateResult(context.getTargetFileDirectory());
}
catch (IOException e)
{
throw new GeneratorException("base generator failed", e);
}
}

@Override
protected boolean needMyHelp(GeneratorContext context)
{
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component("componentGenerator")
public class ComponentGenerator extends AbstractGenerator
{

@Override
protected void generateMyPart(GeneratorContext context)
{
context.getComponentList().forEach(System.out::println);
context.setGenerateResult(context.getTargetFileDirectory());
}

@Override
protected boolean needMyHelp(GeneratorContext context)
{
return CollectionUtils.isNotEmpty(context.getComponentList());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component("uploadGenerator")
public class UploadGenerator extends AbstractGenerator
{
@Override
protected void generateMyPart(GeneratorContext context)
{
context.getUploadFileList().stream().map(Path::getFileName).forEach(System.out::println);
context.setGenerateResult("handle upload file success");
}

@Override
protected boolean needMyHelp(GeneratorContext context)
{
return CollectionUtils.isNotEmpty(context.getUploadFileList());
}
}

在实际的开发中,还可以定义一个统一的异常,用于异常管理,在generateMyPart()方法中抛出,由上层捕获处理。

1
2
3
4
5
6
7
8
9
10
11
12
public class GeneratorException extends RuntimeException
{
public GeneratorException(final String message)
{
super(message);
}

public GeneratorException(final String message, final Throwable cause)
{
super(message, cause);
}
}

接下来是构造责任链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class GeneratorChainBuilder
{
@Resource(name = "baseGenerator")
private AbstractGenerator baseGenerator;

@Resource(name = "componentGenerator")
private AbstractGenerator componentGenerator;

@Resource(name = "uploadGenerator")
private AbstractGenerator uploadGenerator;

public AbstractGenerator buildChain()
{
baseGenerator.setNextGenerator(componentGenerator).setNextGenerator(uploadGenerator);
return baseGenerator;
}
}

接下来是service类,定义service类,封装责任链:

1
2
3
4
public interface GeneratorService
{
void generatorProject(GeneratorContext context);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class GeneratorServiceImpl implements GeneratorService
{

@Autowired
private GeneratorChainBuilder chainBuilder;

@Override
public void generatorProject(GeneratorContext context)
{
AbstractGenerator generatorChain = chainBuilder.buildChain();
generatorChain.generateProject(context);
}
}

最后是使用者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class GeneratorController
{

@Autowired
private GeneratorService generatorService;

public String generate(@RequestParam ...)
{

// 可以有一些预处理

// 构造上下文
GeneratorContext context = new GeneratorContext();
// 根据入参,调用context的setter方法,设置上下文
context.setTemplateId("1");
context.setLocalFilePaths(targetFilePaths);
context.setComponentList(componentList);
// ..省略其他setter

generatorService.generatorProject(context);
return context.getGenerateResult();
}
}

此后,如果要加入新的功能,只需新建责任者,继承自AbstractGenerator类,实现相关方法,并在责任链构造器中在合适的顺序位置加入该新的责任者即可。

如果后面的责任者不需要进行处理,也就无法设置上下文信息中的result值,返回的result值只有需要处理的责任者才可设置,这样就解决了前面提到的返回值的问题。

这里其实还可以优化,就是每个责任者持有一个int类型的字段,表示其处理先后顺序,然后autowired注入到一个map或者list中,根据该字段进行排序,然后依次处理,其实这样也有点像之前写到的“工厂模式”了。

Read More

[1]Chain of Responsibility Design Pattern in Java
[2]责任链模式
[3]模板方法模式
[4]Spring注解——同一接口有多个实现类,如何注入?@Resource、@Autowired、@Qualifier