首页 > 其他分享 >通过策略模式实现树状结构的自动生成

通过策略模式实现树状结构的自动生成

时间:2022-09-27 14:00:28浏览次数:45  
标签:return 树状 Tree children 生成 自动 节点 root public

1.树处理器

public abstract class AbstractTreeHandler implements InitializingBean {
    @Resource
    private TreeHandlerContext treeContext;

    @Override
    public void afterPropertiesSet() throws Exception {
        treeContext.putTreeHandler(getTreeClassPath(), this);
    }

    public abstract String getTreeClassPath();

    /**
     * 搜索子节点的方法。
     * 根据传入的节点,通过{@link AbstractTreeHandler#autoGenerateTree(Tree)}自动构件树。
     * 该方法的作用是根据传入的节点,搜索该节点下的子节点。
     * @param root 需要搜索子节点的父节点
     * @return
     * @throws Exception
     */
    protected abstract List<? extends Tree> searchChildren(Tree root) throws Exception;

        /**
     * 自动生成树的方法,根须抽象方法{@link AbstractTreeHandler#searchChildren(Tree)}自动生成
     * @param root 根节点,自动生成该节点下的树
     * @throws Exception
     */
    public void autoGenerateTree(Tree root) throws Exception {
        if (this.isFinishSearch(root)) {
            return;
        }
        //noinspection unchecked
        List<Tree> children = (List<Tree>) this.searchChildren(root);
        root.setChildren(children);
        if (!CollectionUtils.isEmpty(children)) {
            for (Tree child : children) {
                this.autoGenerateTree(child);
            }
            root.setIsLeaf(false);
        } else {
            root.setIsLeaf(true);
        }
    }
    
    /**
     * 停止搜索子节点的方法,默认不停止,有需求需要实现
     * @param tree
     * @return
     */
    protected boolean isFinishSearch(Tree tree) {
        return false;
    }

    /**
     * 树排序方法,根据树的每一级的sort字段进行排序
     * @param sourceTree 需要进行排序的树
     * @param <T> {@link Tree} 的子类
     */
    public <T extends Tree> void sort(T sourceTree) {
        List<? extends Tree> children = sourceTree.getChildren();
        if (!CollectionUtil.isEmpty(children)) {
            children.sort((i1, i2) -> {
                int sort1 = ObjectUtils.isEmpty(i1.getSort()) ? 0 : i1.getSort();
                int sort2 = ObjectUtils.isEmpty(i2.getSort()) ? 0 : i2.getSort();
                return sort1 - sort2;
            });
            sourceTree.setChildren(children);
            for (Tree child : children) {
                this.sort(child);
            }
        }
    }

    /**
     * 树节点过滤器,如果需要特殊实现可重写该方法
     * @param sourceTree 进行过滤的树
     * @param jsonStr 树属性的JSON对象,对JSON内容进行like过滤
     * @param <T> {@link Tree}的子类
     * @return
     */
    public <T extends Tree> boolean filter(T sourceTree, String jsonStr) {
        return filter(sourceTree, i -> {
            try {
                JSONObject paramMap = JSONObject.parseObject(jsonStr);
                boolean canRemove = true;
                Class<? extends Tree> treeClass = i.getClass();
                for (String key : paramMap.keySet()) {
                    if (StringUtils.hasLength(paramMap.get(key).toString())){
                        Field field = treeClass.getDeclaredField(key);
                        field.setAccessible(true);
                        String fieldValue;
                        if (StringUtils.hasLength(field.get(i).toString())){
                            fieldValue = field.get(i).toString();
                        } else {
                            fieldValue = "";
                        }
                        canRemove &= fieldValue.contains(paramMap.get(key).toString());
                    }
                }
                return canRemove;
            } catch (Exception e){
                return false;
            }
        });
    }

/**
     * 子节点过滤器。根据过滤条件从根节点开始逐级往下过滤,如果某一节点符合过滤条件,
     * 则自该节点往上全部保留,如果该节点下的节点都不符合过滤条件,则全部移除
     * @param sourceTree 需要操作的根节点
     * @param predicate  过滤条件
     */
    @SuppressWarnings("unchecked")
    public <T extends Tree> boolean filter(T sourceTree, Predicate<T> predicate) {
        boolean remove = true;
        if (sourceTree.getChildren() != null && sourceTree.getChildren().size() > 0) {
            Iterator<? extends Tree> iterator = sourceTree.getChildren().iterator();
            for (sourceTree.getChildren().iterator(); iterator.hasNext(); ) {
                T next = (T) iterator.next();
                boolean canRemove = this.filter(next, predicate);
                if (canRemove) {
                    iterator.remove();
                }
                if (predicate.test(sourceTree)) {
                    canRemove = false;
                }
                remove = remove && canRemove;
            }
        } else {
            remove = !predicate.test(sourceTree);
        }
        return remove;
    }

/**
     * 根据已经生成的树构建一个可供前端使用的级联对象,该方法会忽略根节点。
     * 如果需要同时生成根节点的相关级联信息,可使用{@link AbstractTreeHandler#getCascader(Tree, Function, Function)}方法
     * @param root 生成级联对象的树
     * @param label 级联对象的展示文本
     * @param value 级联对象的实际绑定的值
     * @param <T> {@link Tree}的子类
     * @return 一个默认键值对为label和value的map集合
     */
    public <T extends Tree> List<Map<String, Object>> getCascaderIgnoreRoot(T root, Function<T, String> label, Function<T, String> value) {
        List<Map<String, Object>> mapList = new ArrayList<>();
        List<? extends Tree> children = root.getChildren();
        if (!CollectionUtils.isEmpty(children)) {
            for (Tree child : children) {
                //noinspection unchecked
                Map<String, Object> cascader = this.getCascader((T) child, label, value);
                mapList.add(cascader);
            }
        }
        return mapList;
    }

/**
     * @see AbstractTreeHandler#getCascaderIgnoreRoot(Tree, Function, Function)
     */
    public <T extends Tree> Map<String, Object> getCascader(T root, Function<T, String> label, Function<T, String> value) {
        Map<String, Object> map = new HashMap<>();
        map.put("label", label.apply(root));
        map.put("value", value.apply(root));
        List<? extends Tree> children = root.getChildren();
        if (!CollectionUtils.isEmpty(children)) {
            List<Map<String, Object>> childrenMap = new ArrayList<>();
            for (Tree child : children) {
                //noinspection unchecked
                Map<String, Object> cascader = this.getCascader((T) child, label, value);
                childrenMap.add(cascader);
            }
            map.put("children", childrenMap);
        }
        return map;
    }
      
}
View Code

2.TreeHandlerContext

@Component
public class TreeHandlerContext {
    private final Map<String, AbstractTreeHandler> treeHandlerMap = new HashMap<>();

    public void putTreeHandler(String treeClassPath, AbstractTreeHandler treeHandler) {
        treeHandlerMap.put(treeClassPath, treeHandler);
    }

    public AbstractTreeHandler getTreeHandler(String treeClassPath) {
        AbstractTreeHandler treeHandler = this.treeHandlerMap.get(treeClassPath);
        if (treeHandler == null) {
            throw new TreeNotFoundException("Can not found this tree: " + treeClassPath);
        }
        return treeHandler;
    }
}

3.树的基础结构

@Data
@Accessors(chain = true)
public class Tree {
    private String id;
    private String name;
    private String code;
    private String remark;
    private String parentID;
    private Integer sort;
    private Boolean isLeaf;
    private List<? extends Tree> children;
}

4.实现searchChildren自动生成树

4.1继承AbstractTreeHandler类实现抽象接口getTreeClassPath和searchChildren

@Component
public class SomethingTreeHandler extends AbstractTreeHandler{
    @Override
    public String getTreeClassPath() {
        return SomethingTree.class.getName();
    }

    @Override
    protected List<? extends Tree> searchChildren(Tree root) throws Exception {
        List<SomethingTree> children = new ArrayList<>();
        if (root.getId().length() <= 5){
            for (int i = 0; i < 2; i++) {
                SomethingTree child = new SomethingTree();
                child.setId(root.getId() + i)
                        .setName(root.getName() + i)
                        .setCode(root.getCode() + i);
                child.setSomething("something");
                child.setIsLeaf(child.getId().length()>5);
                children.add(child);
            }
        }
        return children;
    }
}
/**
* 具体实现的树需要继承树的基础结构
*/
@Data public class SomethingTree extends Tree{ private String something; }

4.2调用

    @Autowired
    private TreeHandlerContext treeHandlerContext;

    @Test
    public void t0() throws Exception {
        SomethingTree root = new SomethingTree();
        root.setId("0")
                .setName("Tree0")
                .setCode("Tree0");
        AbstractTreeHandler treeHandler = treeHandlerContext.getTreeHandler(SomethingTree.class.getName());
        treeHandler.autoGenerateTree(root);
        System.out.println(JSON.toJSONString(root));
    }

在实现getTreeClassPath方法时使用的键名即获取该处理器的键名

返回的树结构如下

 

标签:return,树状,Tree,children,生成,自动,节点,root,public
From: https://www.cnblogs.com/ffaann/p/16734322.html

相关文章

  • mybatisPlus逆向生成工具类CodeGenerator (生成pojo、controller、service等)
    importcom.baomidou.mybatisplus.annotation.IdType;importcom.baomidou.mybatisplus.generator.AutoGenerator;importcom.baomidou.mybatisplus.generator.config.Da......
  • php 微信支付V3API 签名生成
    classFormatter{/***GeneratearandomBASE62stringaka`nonce`,similaras`random_bytes`.**@paramint$size-Noncestringlength,......
  • 【Echarts】用java在后台生成图片
    一、场景需求前端不用等待获取数据渲染echarts图表,直接请求后台,获取echarts图表的base64编码,生成本地图片。二、使用工具1.下载:Phantomjs:https://phantomjs.org/download.......
  • 软件的自动更新实现
    【转载自】https://www.cnblogs.com/meteorcui/archive/2005/11/16/2021123.html1.思路:使用WebService来实现这一功能,Webservice中放一xml文件,用于存储版本和需要更新......
  • WEB自动化_元素等待
    元素等待为什么设置元素等待由于电脑配置或网络原因,在查找元素时,元素代码未在第一时间内被加载出来,而抛出未找到元素异常。定义在定位页面元素时如果未找到,会在指定......
  • WEB自动化测试_鼠标和键盘操作
    鼠标和键盘操作鼠标操作方法说明在Selenium中将操作鼠标的方法封装在ActionChains类中实例化对象action=ActionChains(driver)方法context_click(element).pe......
  • MybatisPlus学习之MyBatisX插件比代码生成器还好用的哦2
    概述  https://blog.csdn.net/qq_39746820/article/details/124339612MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率但是在真正开发过程中,M......
  • mybatisplus 代码生成器
    pom.xml<!--mybitsplus启动器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</......
  • 分布式ID详解(5种分布式ID生成方案)
    分布式架构会涉及到分布式全局唯一ID的生成,今天我就来详解分布式全局唯一ID,以及分布式全局唯一ID的实现方案@mikechen什么是分布式系统唯一ID在复杂分布式系统中,往往需......
  • MybatisPlus学习之MyBatisX插件比代码生成器还好用的哦
    一、MyBatisX的作用:1.xml跳转2.生成代码3.重置代码4.JAP提示跟代码生成器比较:代码生成器生成文件还有controller等文件,而mybatsx没有三、如何使用:1.安装MyBatisX:安装方......