国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【設(shè)計模式】組合模式實現(xiàn)部門樹實踐

這篇具有很好參考價值的文章主要介紹了【設(shè)計模式】組合模式實現(xiàn)部門樹實踐。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

1.前言

幾乎在每一個系統(tǒng)的開發(fā)過程中,都會遇到一些樹狀結(jié)構(gòu)的開發(fā)需求,例如:組織機(jī)構(gòu)樹,部門樹,菜單樹等。只要是需要開發(fā)這種樹狀結(jié)構(gòu)的需求,我們都可以使用組合模式來完成。

本篇將結(jié)合組合模式與Mysql實現(xiàn)一個部門樹,完成其增刪改和樹形結(jié)構(gòu)的組裝。

2.組合模式

組合模式是一種結(jié)構(gòu)型設(shè)計模式,它允許我們將對象組合成樹形結(jié)構(gòu)來表現(xiàn)部分-整體的層次結(jié)構(gòu)。以部門樹為例,我們可以將上級部門與下級部門組合起來,形成一個單邊樹,用代碼來描述的話,就是這個樣子的:

public class DeptNode {
    private List<DeptNode> children = new ArrayList<>();
}

提供一個部門節(jié)點(diǎn)類,里面會有一個集合,用于保存當(dāng)前部門的下級部門,同理在children這個集合中的部門節(jié)點(diǎn),也可能會有它的下級部門節(jié)點(diǎn)。

當(dāng)然,這不是實現(xiàn)組合模式的唯一方式,還有其他復(fù)雜一點(diǎn)方式,會區(qū)分不同的節(jié)點(diǎn)類型,是根節(jié)點(diǎn)、分支節(jié)點(diǎn)、還是葉子節(jié)點(diǎn)等。這里之所以做這種簡單的設(shè)計,是因為我們的樹狀結(jié)構(gòu)的數(shù)據(jù)一般都會交給前端去做渲染,在很多前端的組件庫中,就是用這種簡單的方式來組織樹的,例如在Element-UI中的樹狀結(jié)構(gòu):
【設(shè)計模式】組合模式實現(xiàn)部門樹實踐,# 設(shè)計模式,架構(gòu)與設(shè)計,設(shè)計模式,組合模式

3.實現(xiàn)方式

3.1.數(shù)據(jù)結(jié)構(gòu)設(shè)計

先看數(shù)據(jù)庫的設(shè)計,數(shù)據(jù)庫必要的字段比較簡單,直接看一下建表的sql:

create table dept
(
    id        bigint auto_increment comment '部門id'
        primary key,
    parent_id bigint       null comment '上級部門id',
    name      varchar(200) null comment '部門名稱',
    tree_path varchar(255) null comment '樹路徑'
)

idparent_id很好理解,主要是用來維護(hù)部門的上下級關(guān)系,name不解釋,tree_path這個字段其實不是必須要的,沒有它也可以實現(xiàn)部門樹,但是加上這個path之后,可以比較方便的查詢子樹。


PO對象與數(shù)據(jù)庫字段保持一致,這里就不過多贅述,代碼中需要返回給前端的樹對象要修改一下字段名,name->label

@Getter
@Setter
public class DeptNode {

    private List<DeptNode> children = new ArrayList<>();

    private Long id;
    private Long parentId;
    private String label;
    private String treePath;
}

3.2.數(shù)據(jù)新增

由于是自增主鍵,數(shù)據(jù)的新增需要再保存之后獲取到主鍵id,再更新treePath。
這里為了方便,我用了dept對象直接透傳,使用的是mybatis-plus操作數(shù)據(jù)庫,可以替換成自己喜歡的ORM。

@Service("deptService")
public class DeptServiceImpl extends ServiceImpl<DeptDao, Dept> implements DeptService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insert(Dept dept) {
        // 如果有上級部門id,則獲取上級機(jī)構(gòu)
        Dept parentDept = null;
        if (dept.getParentId() != null) {
            parentDept = this.getById(dept.getParentId());
            // 上級機(jī)構(gòu)不能為空
            if (parentDept == null) {
                throw new RuntimeException("上級機(jī)構(gòu)不存在");
            }
        }
        // MybatisPlus新增后可以獲取主鍵
        this.save(dept);

        // 更新樹路徑
        if (parentDept != null) {
            dept.setTreePath(parentDept.getTreePath() + dept.getId() + "/");
        } else {
            dept.setTreePath("/" + dept.getId() + "/");
        }
        this.updateById(dept);
    }
}

3.2.數(shù)據(jù)更新

數(shù)據(jù)更新需要注意兩個點(diǎn):

  • 新的上級部門不能是自己,也不能是自己的子部門(避免成環(huán))。
  • 更新樹路徑之后,樹路徑上的所有子部門都需要更新樹路徑。
@Override
@Transactional(rollbackFor = Exception.class)
public void update(Dept dept) {
    Dept newParentDept = null;
    if (dept.getParentId() != null) {
        newParentDept = this.getById(dept.getParentId());
        if (newParentDept == null) {
            throw new RuntimeException("上級部門不存在");
        }
        if (newParentDept.getTreePath().contains("/" + dept.getId() + "/")) {
            throw new RuntimeException("上級部門不能是自己或子部門");
        }
    }
    this.updateById(dept);

    // 組裝新的樹路徑
    String newTreePath = (newParentDept == null ? "" : newParentDept.getTreePath()) + dept.getId() + "/"; + dept.getId() + "/";
    // 獲取原有的樹路徑
    String oldTreePath = this.getById(dept.getId()).getTreePath();

    // 獲取所有子部門(循環(huán)更新也可以替換為使用Mysql的replace函數(shù)批量更新)
    LambdaQueryWrapper<Dept> queryWrapper = new LambdaQueryWrapper<>();
    // likeRight表示右模糊查詢,即以oldTreePath開頭的
    queryWrapper.likeRight(Dept::getTreePath, oldTreePath);
    this.list(queryWrapper).forEach(childDept -> {
        // 更新子部門的樹路徑
        childDept.setTreePath(childDept.getTreePath().replace(oldTreePath, newTreePath));
        this.updateById(childDept);
    });
}

上面的循環(huán)更新在數(shù)據(jù)量不大的時候可以這么做,如果量較大的話,推薦使用mysql中的replace函數(shù)替換:

update dept set tree_path = replace(tree_path,'舊路徑','新路徑')
where tree_path like '舊路徑%'

sql中的舊路徑,新路徑替換為上面代碼中獲取到的路徑即可。

3.4.部門樹組裝

部門樹組裝只需要把需要組裝的部門列表查詢出來,然后根據(jù)parent_id的關(guān)聯(lián)關(guān)系組裝數(shù)據(jù)即可。這里tree_path就可以派上用場了,如果只有parent_id的話,要么必須全量查詢所有的部門再過濾,要么需要根據(jù)parent_id做遞歸查詢,而通過tree_path可以直接做右模糊查詢,查詢到的部門都是需要的部門。

我們可以在接口中接收一個部門的id,把這個部門作為部門子樹的根節(jié)點(diǎn):

@Override
public List<DeptNode> tree(Long id) {
    // 傳入了主鍵id,則通過主鍵id對于treePath做右模糊查詢,沒有傳入主鍵id,則查詢所有
    List<Dept> list;
    if (id != null) {
        Dept baseDept = this.getById(id);
        list = this.list(new LambdaQueryWrapper<Dept>().likeRight(Dept::getTreePath, baseDept.getTreePath()));
    } else {
        list = this.list();
    }

    // 將Dept轉(zhuǎn)換為DeptNode
    List<DeptNode> deptNodes = new ArrayList<>();
    for (Dept dept : list) {
        DeptNode deptNode = BeanUtil.copyProperties(dept, DeptNode.class);
        deptNode.setLabel(dept.getName());
        deptNodes.add(deptNode);
    }

    // 循環(huán)遍歷,將子節(jié)點(diǎn)放入父節(jié)點(diǎn)的children中
    for (DeptNode node : deptNodes) {
        deptNodes.stream().filter(item -> node.getId().equals(item.getParentId())).forEach(item -> {
            if (node.getChildren() == null) {
                node.setChildren(CollUtil.newArrayList(item));
            } else {
                node.getChildren().add(item);
            }
        });
    }

    // 返回根節(jié)點(diǎn)
    return deptNodes.stream()
            .filter(item -> item.getParentId() == null || item.getId().equals(id))
            .collect(Collectors.toList());
}

4.測試

通過一個Controller接口發(fā)起測試:

@RestController
@RequestMapping("dept")
public class DeptController {

    @Resource
    private DeptService deptService;

    @PostMapping("insert")
    public void insert(@RequestBody @Valid Dept dept) {
        this.deptService.insert(dept);
    }

    @PostMapping("update")
    public void update(@RequestBody @Valid Dept dept) {
        this.deptService.update(dept);
    }

    @PostMapping("/tree")
    public List<DeptNode> tree(Long id) {
        return this.deptService.tree(id);
    }
}

4.1.部門新增

按照下面的請求參數(shù)順序發(fā)起insert請求,為了驗證的方便,這里的部門加了數(shù)字后綴:

{
  "parentId": null,
  "name": "根部門"
}
{
  "parentId": 1,
  "name": "一級部門-1"
}
{
  "parentId": 1,
  "name": "一級部門-2"
}
{
  "parentId": 2,
  "name": "二級部門-1-1"
}
{
  "parentId": 3,
  "name": "二級部門-2-1"
}
{
  "parentId": 5,
  "name": "三級部門-2-1-1"
}
{
  "parentId": 5,
  "name": "三級部門-2-1-2"
}

執(zhí)行后數(shù)據(jù)的結(jié)果如下,我們可以看到tree_path已經(jīng)正常添加好了:
【設(shè)計模式】組合模式實現(xiàn)部門樹實踐,# 設(shè)計模式,架構(gòu)與設(shè)計,設(shè)計模式,組合模式
通過tree接口,不傳id獲取到的樹結(jié)構(gòu)如下,按照上面說的部門后綴進(jìn)行對比驗證,可以看出部門樹已經(jīng)正確組裝了。

[
    {
        "children": [
            {
                "children": [
                    {
                        "children": [],
                        "id": 4,
                        "parentId": 2,
                        "label": "二級部門-1-1",
                        "treePath": "/1/2/4/"
                    }
                ],
                "id": 2,
                "parentId": 1,
                "label": "一級部門-1",
                "treePath": "/1/2/"
            },
            {
                "children": [
                    {
                        "children": [
                            {
                                "children": [],
                                "id": 6,
                                "parentId": 5,
                                "label": "三級部門-2-1-1",
                                "treePath": "/1/3/5/6/"
                            },
                            {
                                "children": [],
                                "id": 7,
                                "parentId": 5,
                                "label": "三級部門-2-1-2",
                                "treePath": "/1/3/5/7/"
                            }
                        ],
                        "id": 5,
                        "parentId": 3,
                        "label": "二級部門-2-1",
                        "treePath": "/1/3/5/"
                    }
                ],
                "id": 3,
                "parentId": 1,
                "label": "一級部門-2",
                "treePath": "/1/3/"
            }
        ],
        "id": 1,
        "parentId": null,
        "label": "根部門",
        "treePath": "/1/"
    }
]

4.2.部門修改

假設(shè)現(xiàn)在我想把二級部門-2-1直接掛接到根部門下,則兩個三級部門也會跟著一起遷移,嘗試一下做這個修改,請求參數(shù)如下:

{
  "id": 5,
  "parentId": null,
  "name": "二級部門-2-1(改)"
}

執(zhí)行后,數(shù)據(jù)庫的結(jié)果如下,tree_path中間的/3/已經(jīng)去掉了:
【設(shè)計模式】組合模式實現(xiàn)部門樹實踐,# 設(shè)計模式,架構(gòu)與設(shè)計,設(shè)計模式,組合模式

4.3.子樹查詢

傳入二級部門-2-1(改)的id,查詢子樹,期望可以返回三個部門,一個父部門,兩個子部門,請求tree接口的結(jié)果與期望相符:

[
    {
        "children": [
            {
                "children": [],
                "id": 6,
                "parentId": 5,
                "label": "三級部門-2-1-1",
                "treePath": "/1/5/6/"
            },
            {
                "children": [],
                "id": 7,
                "parentId": 5,
                "label": "三級部門-2-1-2",
                "treePath": "/1/5/7/"
            }
        ],
        "id": 5,
        "parentId": 1,
        "label": "二級部門-2-1(改)",
        "treePath": "/1/5/"
    }
]

5.結(jié)語

通過組合模式加上一點(diǎn)數(shù)據(jù)庫的設(shè)計,可以實現(xiàn)大部分常規(guī)的樹狀結(jié)構(gòu)的需求,希望對大家能有所幫助。文章來源地址http://www.zghlxwxcb.cn/news/detail-706999.html

到了這里,關(guān)于【設(shè)計模式】組合模式實現(xiàn)部門樹實踐的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Python入門【?編輯、組合、設(shè)計模式_工廠模式實現(xiàn) 、設(shè)計模式_單例模式實現(xiàn)、工廠和單例模式結(jié)合、異常是什么?異常的解決思路 】(十七)

    Python入門【?編輯、組合、設(shè)計模式_工廠模式實現(xiàn) 、設(shè)計模式_單例模式實現(xiàn)、工廠和單例模式結(jié)合、異常是什么?異常的解決思路 】(十七)

    ??作者簡介:大家好,我是愛敲代碼的小王,CSDN博客博主,Python小白 ??系列專欄:python入門到實戰(zhàn)、Python爬蟲開發(fā)、Python辦公自動化、Python數(shù)據(jù)分析、Python前后端開發(fā) ??如果文章知識點(diǎn)有錯誤的地方,請指正!和大家一起學(xué)習(xí),一起進(jìn)步?? ??如果感覺博主的文章還不錯的

    2024年02月14日
    瀏覽(19)
  • 設(shè)計模式——組合模式

    設(shè)計模式——組合模式

    組合模式(Composite Pattern):組合多個對象形成樹形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式,它是一種對象結(jié)構(gòu)型模式。 組合模式將

    2024年02月11日
    瀏覽(32)
  • 設(shè)計模式——組合模式08

    設(shè)計模式——組合模式08

    組合模式:把類似對象或方法組合成結(jié)構(gòu)為樹狀的設(shè)計思路。 例如部門之間的關(guān)系。 設(shè)計模式,一定要敲代碼理解 滿足開閉原則。葉子結(jié)點(diǎn)與非葉子結(jié)點(diǎn)都繼承或?qū)崿F(xiàn)同一抽象,只是葉子功能權(quán)限少,而非葉子結(jié)點(diǎn)需要容器記錄子節(jié)點(diǎn)。 代碼下載

    2024年04月11日
    瀏覽(25)
  • 設(shè)計模式-組合模式

    設(shè)計模式-組合模式

    組合模式可以使用一棵樹來表示 組合模式使得用戶可以使用一致的方法操作單個對象和組合對象 組合模式又叫部分整體模式,將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),可以更好的實現(xiàn)管理操作,部分-整體對象的操作基本一樣,但是應(yīng)該還會有不一樣的地方

    2024年02月10日
    瀏覽(26)
  • 設(shè)計模式:組合模式

    組合模式是一種結(jié)構(gòu)型設(shè)計模式,用于將對象組織成樹形結(jié)構(gòu),以表示“部分-整體”的層次結(jié)構(gòu)。組合模式使得客戶端可以統(tǒng)一地處理單個對象和組合對象,而不需要區(qū)分它們之間的差異。 在組合模式中,有兩種主要類型的對象:葉節(jié)點(diǎn)和組合節(jié)點(diǎn)。葉節(jié)點(diǎn)表示樹結(jié)構(gòu)中的

    2024年02月07日
    瀏覽(24)
  • 設(shè)計模式--組合模式

    設(shè)計模式--組合模式

    某日,小明公司最近接到一個辦公管理系統(tǒng)的項目,并且在每個城市都有分部。這屬于是很常見的OA系統(tǒng),只要前期將需求分析完善好,中后期開發(fā)維護(hù)是不難的。 然而,總部公司使用后覺得很OK,想要其他城市的分公司也執(zhí)行使用。但是現(xiàn)在的問題是,其他分公司的部門和

    2024年01月21日
    瀏覽(27)
  • 設(shè)計模式——10. 組合模式

    組合模式是一種結(jié)構(gòu)型設(shè)計模式,用于將對象組合成樹狀結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。它允許客戶端以一致的方式處理單個對象和組合對象(包含多個對象的容器),使得客戶端無需關(guān)心它們之間的差異。 組合模式通常涉及兩種主要角色: 葉子節(jié)點(diǎn)(Leaf):這是組

    2024年02月07日
    瀏覽(18)
  • 設(shè)計模式淺析(十) ·設(shè)計模式之迭代器&組合模式

    設(shè)計模式淺析(十) ·設(shè)計模式之迭代器&組合模式

    日常叨逼叨 java設(shè)計模式淺析,如果覺得對你有幫助,記得一鍵三連,謝謝各位觀眾老爺???? 案例 有兩家門店,門店A呢只提供早餐,門店B呢只提供午餐,有一天這兩家店鋪想要進(jìn)行合并,一起做大做強(qiáng),再創(chuàng)輝煌。 合并后呢,對于菜單的定制存在了一定的問題: 門店A的

    2024年04月11日
    瀏覽(23)
  • 結(jié)構(gòu)型設(shè)計模式之組合模式【設(shè)計模式系列】

    結(jié)構(gòu)型設(shè)計模式之組合模式【設(shè)計模式系列】

    C++技能系列 Linux通信架構(gòu)系列 C++高性能優(yōu)化編程系列 深入理解軟件架構(gòu)設(shè)計系列 高級C++并發(fā)線程編程 設(shè)計模式系列 期待你的關(guān)注哦?。。?現(xiàn)在的一切都是為將來的夢想編織翅膀,讓夢想在現(xiàn)實中展翅高飛。 Now everything is for the future of dream weaving wings, let the dream fly in reali

    2024年02月15日
    瀏覽(21)
  • 【23種設(shè)計模式】組合模式【?】

    【23種設(shè)計模式】組合模式【?】

    個人主頁 :金鱗踏雨 個人簡介 :大家好,我是 金鱗 ,一個初出茅廬的Java小白 目前狀況 :22屆普通本科畢業(yè)生,幾經(jīng)波折了,現(xiàn)在任職于一家國內(nèi)大型知名日化公司,從事Java開發(fā)工作 我的博客 :這里是CSDN,是我學(xué)習(xí)技術(shù),總結(jié)知識的地方。希望和各位大佬交流,共同進(jìn)

    2024年02月09日
    瀏覽(25)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包