实现代码可扩展的另一个示例

Posted 编程大观园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现代码可扩展的另一个示例相关的知识,希望对你有一定的参考价值。

如何识别变化呢? 相似模式的代码累积、膨胀。

引语

“代码可扩展的一个小技巧” 一文中谈到,要实现代码可扩展性,需要能够识别变化。

如何识别变化呢? 有两个症状:

  • 相似模式的代码累积、膨胀;

  • switch 语句,每个分支有相似的代码。

通过这两点,就能判别出变化所在。现在变化频度越高,将来就更容易变化。


例子

下面是另一个例子。

新入侵检测里,需要存储大量 agent 上报的信息,比如进程树、文件、脚本、webfile、网络连接信息等。 代码如下:


// 进程树
List<ProcessTreeDTO> processTree = singleDetectionDetail.getProcessTree();
if (CollectionUtils.isNotEmpty(processTree)) 
    elementDOList.addAll(buildProcessTrees(processTree));


// 文件信息
List<FileInfoDTO> file = singleDetectionDetail.getFile();
if (CollectionUtils.isNotEmpty(file)) 
    elementDOList.addAll(buildFileInfos(agentId, file, fhashMap));


// 脚本文件
List<ScriptDTO> scripts = singleDetectionDetail.getScripts();
if (CollectionUtils.isNotEmpty(scripts)) 
    elementDOList.addAll(buildScripts(agentId, scripts, fhashMap));


// web文件
List<WebfileDTO> webfile = singleDetectionDetail.getWebfile();
if (CollectionUtils.isNotEmpty(webfile)) 
    elementDOList.addAll(buildWebFile(agentId, webfile, fhashMap));

显然,这样累积,这个类会膨胀得很厉害,也容易产生冲突。 如何重构呢?

通过代码分析,很容易就能知道,这些代码遵循相似的模式: 将一些上报的数据转换成元素对象列表。 据此,就可以定义接口:


/**
 * 元素构建器
 */
public interface ElementBuilder 

    /**
     * 是否必要构建
     * @param context 元素构建上下文语境
     * @return 是否必要构建详情
     */
    boolean need(ElementBuilderContext context);

    /**
     * 元素构建器
     * @param context 元素构建上下文语境
     * @return 构建的元素
     */
    List<ElementDO> buildFrom(ElementBuilderContext context);




有童鞋可能会问: 明明参数只要 DTO 对象列表、agentId、fhashMap ,为什么要定义一个 ElementBuilderContext 呢?

其实,这是最终的结果。在重构的过程中,显然不是一步到位的。比如,我可能最开始只定义了 List buildFrom(LIst dtoList); 随后发现这样不够,必须定义成 List buildFrom(LIst dtoList, String agentId, Map<String, HashDTO> fhashMap); 但这样仍然是不够的。因为很难预想后面还需要什么信息。因此,定义一个 Context 参数对象就是一个常用实践。

有了这个定义,原有的代码就很容易转换成一个组件类:



/**
 * 文件元素构建
 * Created by qinshu on 2022/3/9
 *
 * 源代码由许文进编写,由舒琴重构
 */
@Component
public class FileBuilder implements ElementBuilder 

    /**
     * 构建文件信息
     */
    @Override
    public List<ElementDO> buildFrom(ElementBuilderContext context) 
       // codes for build elements
    

    @Override
    public boolean need(ElementBuilderContext context) 
        SingleDetectionDetail agentDetectionDetail = context.getDetail();
        return CollectionUtils.isNotEmpty(agentDetectionDetail.getFile());
    

最终,元素构建流程为:


ElementBuilderContext context = ElementBuilderContext.builder()
        .agentId(agentId)
        .xxx(xxx)
        .build();

// 元素构建

for(ElementBuilder elementBuilder: elementBuilders) 
    if (elementBuilder.need(context)) 
        elementDOList.addAll(elementBuilder.buildFrom(context));
    


后续再需要构建新元素,只要添加实现 ElementBuilder 的子类即可。实现了开闭原则。


以上是关于实现代码可扩展的另一个示例的主要内容,如果未能解决你的问题,请参考以下文章

GroovyGroovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )

实现可扩展代码的四步曲

基础扩展 | 16. 队列应用示例:广度优先搜索

设计模式工厂方法模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

PowerShell-自定义函数 Function的另一种写法

根据我的以下示例,如何将数组添加到 Objective c 中的另一个数组?