小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)迭代器模式「模擬公司組織架構(gòu)樹(shù)結(jié)構(gòu)關(guān)系,深度迭代遍歷人員信息輸出場(chǎng)景」

 小傅哥 2021-12-13


作者:小傅哥
博客:https:// - 原創(chuàng)系列專題文章

沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!😄

一、前言

相信相信的力量!

從懵懂的少年,到拿起鍵盤(pán),可以寫(xiě)一個(gè)HelloWorld。多數(shù)人在這并不會(huì)感覺(jué)有多難,也不會(huì)認(rèn)為做不出來(lái)。因?yàn)檫@樣的例子,有老師的指導(dǎo)、有書(shū)本的例子、有前人的經(jīng)驗(yàn)。但隨著你的開(kāi)發(fā)時(shí)間越來(lái)越長(zhǎng),要解決更復(fù)雜的問(wèn)題或者技術(shù)創(chuàng)新,因此在網(wǎng)上搜了幾天幾夜都沒(méi)有答案,這個(gè)時(shí)候是否想過(guò)放棄,還是一直堅(jiān)持不斷的嘗試一點(diǎn)點(diǎn)完成自己心里要的結(jié)果。往往這種沒(méi)有前車(chē)之鑒需要自己解決問(wèn)題的時(shí)候,可能真的會(huì)折磨到要崩潰,但你要愿意執(zhí)著、愿意倔強(qiáng),愿意選擇相信相信的力量,就一定能解決。哪怕解決不了,也可以在這條路上摸索出其他更多的收獲,為后續(xù)前進(jìn)的道路填充好墊腳石。

時(shí)間緊是寫(xiě)垃圾代碼的理由?

擰螺絲?Ctrl+C、Ctrl+V?貼膏藥一樣寫(xiě)代碼?沒(méi)有辦法,沒(méi)有時(shí)間,往往真的是借口,胸中沒(méi)用筆墨,才只能湊合。難道一定是好好寫(xiě)代碼就浪費(fèi)時(shí)間,拼湊CRUD就快嗎,根本不可能的。因?yàn)椴粫?huì),沒(méi)用實(shí)操過(guò),很少架構(gòu)出全場(chǎng)景的設(shè)計(jì),才很難寫(xiě)出優(yōu)良的代碼。多增強(qiáng)自身的編碼(武術(shù))修為,在各種編碼場(chǎng)景中讓自己變得老練,才好應(yīng)對(duì)緊急情況下的需求開(kāi)發(fā)和人員安排。就像韓信一樣有謀有略,才能執(zhí)掌百萬(wàn)雄兵。

不要只是做個(gè)工具人!

因?yàn)槿粘5木帉?xiě)簡(jiǎn)單業(yè)務(wù)需求,導(dǎo)致自己像個(gè)工具人一樣,日久天長(zhǎng)的也就很少去深入學(xué)習(xí)更多技術(shù)棧??匆?jiàn)有工具、有組件、有框架,拿來(lái)就用用,反正沒(méi)什么體量也不會(huì)出什么問(wèn)題。但如果你想要更多的收入,哪怕是重復(fù)的造輪子,你也要去嘗試造一個(gè),就算不用到生產(chǎn),自己玩玩總可以吧。有些事情只有自己經(jīng)歷過(guò),才能有最深的感觸,參與過(guò)實(shí)踐過(guò),才好總結(jié)點(diǎn)評(píng)學(xué)習(xí)。

二、開(kāi)發(fā)環(huán)境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程一個(gè),可以通過(guò)關(guān)注公眾號(hào)bugstack蟲(chóng)洞棧,回復(fù)源碼下載獲取(打開(kāi)獲取的鏈接,找到序號(hào)18)
工程描述
itstack-demo-design-15-00開(kāi)發(fā)樹(shù)形組織架構(gòu)關(guān)系迭代器

三、迭代器模式介紹

迭代器模式,圖片來(lái)自 refactoringguru.cn

迭代器模式,常見(jiàn)的就是我們?nèi)粘J褂玫?code>iterator遍歷。雖然這個(gè)設(shè)計(jì)模式在我們的實(shí)際業(yè)務(wù)開(kāi)發(fā)中的場(chǎng)景并不多,但卻幾乎每天都要使用jdk為我們提供的list集合遍歷。另外增強(qiáng)的for循環(huán)雖然是循環(huán)輸出數(shù)據(jù),但是他不是迭代器模式。迭代器模式的特點(diǎn)是實(shí)現(xiàn)Iterable接口,通過(guò)next的方式獲取集合元素,同時(shí)具備對(duì)元素的刪除等操作。而增強(qiáng)的for循環(huán)是不可以的。

這種設(shè)計(jì)模式的優(yōu)點(diǎn)是可以讓我們以相同的方式,遍歷不同的數(shù)據(jù)結(jié)構(gòu)元素,這些數(shù)據(jù)結(jié)構(gòu)包括;數(shù)組鏈表、樹(shù)等,而用戶在使用遍歷的時(shí)候并不需要去關(guān)心每一種數(shù)據(jù)結(jié)構(gòu)的遍歷處理邏輯,從讓使用變得統(tǒng)一易用。

四、案例場(chǎng)景模擬

在本案例中我們模擬迭代遍歷輸出公司中樹(shù)形結(jié)構(gòu)的組織架構(gòu)關(guān)系中雇員列表

大部分公司的組織架構(gòu)都是金字塔結(jié)構(gòu),也就這種樹(shù)形結(jié)構(gòu),分為一級(jí)、二級(jí)、三級(jí)等部門(mén),每個(gè)組織部門(mén)由雇員填充,最終體現(xiàn)出一個(gè)整體的樹(shù)形組織架構(gòu)關(guān)系。

一般我們常用的遍歷就是jdk默認(rèn)提供的方法,對(duì)list集合遍歷。但是對(duì)于這樣的偏業(yè)務(wù)特性較大的樹(shù)形結(jié)構(gòu),如果需要使用到遍歷,那么就可以自己來(lái)實(shí)現(xiàn)。接下來(lái)我們會(huì)把這個(gè)組織層次關(guān)系通過(guò)樹(shù)形數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn),并完成迭代器功能。

五、迭代器模式遍歷組織結(jié)構(gòu)

在實(shí)現(xiàn)迭代器模式之前可以先閱讀下javalist方法關(guān)于iterator的實(shí)現(xiàn)部分,幾乎所有的迭代器開(kāi)發(fā)都會(huì)按照這個(gè)模式來(lái)實(shí)現(xiàn),這個(gè)模式主要分為以下幾塊;

  1. Collection,集合方法部分用于對(duì)自定義的數(shù)據(jù)結(jié)構(gòu)添加通用方法;add、remove、iterator等核心方法。
  2. Iterable,提供獲取迭代器,這個(gè)接口類會(huì)被Collection繼承。
  3. Iterator,提供了兩個(gè)方法的定義;hasNext、next,會(huì)在具體的數(shù)據(jù)結(jié)構(gòu)中寫(xiě)實(shí)現(xiàn)方式。

除了這樣通用的迭代器實(shí)現(xiàn)方式外,我們的組織關(guān)系結(jié)構(gòu)樹(shù),是由節(jié)點(diǎn)和節(jié)點(diǎn)間的關(guān)系鏈構(gòu)成,所以會(huì)比上述的內(nèi)容多一些入?yún)ⅰ?/p>

1. 工程結(jié)構(gòu)

itstack-demo-design-15-02
└── src
    ├── main
    │   └── java
    │       └── org.itstack.demo.design
    │           ├── group
    │           │├── Employee.java
    │           │├── GroupStructure.java
    │           │└── Link.java
    │           └──  lang
    │            ├── Collection.java
    │            ├── Iterable.java
    │            └── Iterator.java
    └── test
        └── java
            └── org.itstack.demo.design.test
                └── ApiTest.java

迭代器模式模型結(jié)構(gòu)

迭代器模式模型結(jié)構(gòu)

  • 以上是我們工程類圖的模型結(jié)構(gòu),左側(cè)是對(duì)迭代器的定義,右側(cè)是在數(shù)據(jù)結(jié)構(gòu)中實(shí)現(xiàn)迭代器功能。
  • 關(guān)于左側(cè)部分的實(shí)現(xiàn)與jdk中的方式是一樣的,所以在學(xué)習(xí)的過(guò)程中可以互相參考,也可以自己擴(kuò)展學(xué)習(xí)。
  • 另外這個(gè)遍歷方式一個(gè)樹(shù)形結(jié)構(gòu)的深度遍歷,為了可以更加讓學(xué)習(xí)的小伙伴容易理解,這里我實(shí)現(xiàn)了一種比較簡(jiǎn)單的樹(shù)形結(jié)構(gòu)深度遍歷方式。后續(xù)讀者也可以把遍歷擴(kuò)展為橫向遍歷也就是寬度遍歷。

2. 代碼實(shí)現(xiàn)

2.1 雇員實(shí)體類

/**
 * 雇員
 */
public class Employee {

    private String uId;   // ID
    private String name;  // 姓名
    private String desc;  // 備注
    
    // ...get/set
}
  • 這是一個(gè)簡(jiǎn)單的雇員類,也就是公司員工的信息類,包括必要的信息;id、姓名、備注。

2.2 樹(shù)節(jié)點(diǎn)鏈路

/**
 * 樹(shù)節(jié)點(diǎn)鏈路
 */
public class Link {

    private String fromId; // 雇員ID
    private String toId;   // 雇員ID    
    
    // ...get/set
}
  • 這個(gè)類用于描述結(jié)構(gòu)樹(shù)中的各個(gè)節(jié)點(diǎn)之間的關(guān)系鏈,也就是A to B、B to C、B to D,以此描述出一套完整的樹(shù)組織結(jié)構(gòu)。

2.3 迭代器定義

public interface Iterator<E> {

    boolean hasNext();

    E next();
    
}
  • 這里的這個(gè)類和javajdk中提供的是一樣的,這樣也方面后續(xù)讀者可以對(duì)照listIterator進(jìn)行源碼學(xué)習(xí)。
  • 方法描述;hasNext,判斷是否有下一個(gè)元素、next,獲取下一個(gè)元素。這個(gè)在list的遍歷中是經(jīng)常用到的。

2.4 可迭代接口定義

public interface Iterable<E> {

    Iterator<E> iterator();

}
  • 這個(gè)接口中提供了上面迭代器的實(shí)現(xiàn)Iterator的獲取,也就是后續(xù)在自己的數(shù)據(jù)結(jié)構(gòu)中需要實(shí)現(xiàn)迭代器的功能并交給Iterable,由此讓外部調(diào)用方進(jìn)行獲取使用。

2.5 集合功能接口定義

public interface Collection<E, L> extends Iterable<E> {

    boolean add(E e);

    boolean remove(E e);

    boolean addLink(String key, L l);

    boolean removeLink(String key);

    Iterator<E> iterator();

}
  • 這里我們定義集合操作接口;Collection,同時(shí)繼承了另外一個(gè)接口Iterable的方法iterator()。這樣后續(xù)誰(shuí)來(lái)實(shí)現(xiàn)這個(gè)接口,就需要實(shí)現(xiàn)上述定義的一些基本功能;添加元素、刪除元素、遍歷。
  • 同時(shí)你可能注意到這里定義了兩個(gè)泛型<E, L>,因?yàn)槲覀兊臄?shù)據(jù)結(jié)構(gòu)一個(gè)是用于添加元素,另外一個(gè)是用于添加樹(shù)節(jié)點(diǎn)的鏈路關(guān)系。

2.6 (核心)迭代器功能實(shí)現(xiàn)

public class GroupStructure implements Collection<Employee, Link> {

    private String groupId;                                                 // 組織ID,也是一個(gè)組織鏈的頭部ID
    private String groupName;                                               // 組織名稱
    private Map<String, Employee> employeeMap = new ConcurrentHashMap<String, Employee>();  // 雇員列表
    private Map<String, List<Link>> linkMap = new ConcurrentHashMap<String, List<Link>>();  // 組織架構(gòu)關(guān)系;id->list
    private Map<String, String> invertedMap = new ConcurrentHashMap<String, String>();       // 反向關(guān)系鏈

    public GroupStructure(String groupId, String groupName) {
        this.groupId = groupId;
        this.groupName = groupName;
    }

    public boolean add(Employee employee) {
        return null != employeeMap.put(employee.getuId(), employee);
    }

    public boolean remove(Employee o) {
        return null != employeeMap.remove(o.getuId());
    }

    public boolean addLink(String key, Link link) {
        invertedMap.put(link.getToId(), link.getFromId());
        if (linkMap.containsKey(key)) {
            return linkMap.get(key).add(link);
        } else {
            List<Link> links = new LinkedList<Link>();
            links.add(link);
            linkMap.put(key, links);
            return true;
        }
    }

    public boolean removeLink(String key) {
        return null != linkMap.remove(key);
    }

    public Iterator<Employee> iterator() {

        return new Iterator<Employee>() {

            HashMap<String, Integer> keyMap = new HashMap<String, Integer>();

            int totalIdx = 0;
            private String fromId = groupId;  // 雇員ID,From
            private String toId = groupId;   // 雇員ID,To

            public boolean hasNext() {
                return totalIdx < employeeMap.size();
            }

            public Employee next() {
                List<Link> links = linkMap.get(toId);
                int cursorIdx = getCursorIdx(toId);

                // 同級(jí)節(jié)點(diǎn)掃描
                if (null == links) {
                    cursorIdx = getCursorIdx(fromId);
                    links = linkMap.get(fromId);
                }

                // 上級(jí)節(jié)點(diǎn)掃描
                while (cursorIdx > links.size() - 1) {
                    fromId = invertedMap.get(fromId);
                    cursorIdx = getCursorIdx(fromId);
                    links = linkMap.get(fromId);
                }

                // 獲取節(jié)點(diǎn)
                Link link = links.get(cursorIdx);
                toId = link.getToId();
                fromId = link.getFromId();
                totalIdx++;

                // 返回結(jié)果
                return employeeMap.get(link.getToId());
            }
             
            // 給每個(gè)層級(jí)定義寬度遍歷進(jìn)度
            public int getCursorIdx(String key) {
                int idx = 0;
                if (keyMap.containsKey(key)) {
                    idx = keyMap.get(key);
                    keyMap.put(key, ++idx);
                } else {
                    keyMap.put(key, idx);
                }
                return idx;
            }
        };
    }

}
  • 以上的這部分代碼稍微有點(diǎn)長(zhǎng),主要包括了對(duì)元素的添加和刪除。另外最重要的是對(duì)遍歷的實(shí)現(xiàn)new Iterator<Employee>。
  • 添加和刪除元素相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,使用了兩個(gè)map數(shù)組結(jié)構(gòu)進(jìn)行定義;雇員列表、組織架構(gòu)關(guān)系;id->list。當(dāng)元素添加元素的時(shí)候,會(huì)分別在不同的方法中向map結(jié)構(gòu)中進(jìn)行填充指向關(guān)系(A->B),也就構(gòu)建出了我們的樹(shù)形組織關(guān)系。

迭代器實(shí)現(xiàn)思路

  1. 這里的樹(shù)形結(jié)構(gòu)我們需要做的是深度遍歷,也就是左側(cè)的一直遍歷到最深節(jié)點(diǎn)。
  2. 當(dāng)遍歷到最深節(jié)點(diǎn)后,開(kāi)始遍歷最深節(jié)點(diǎn)的橫向節(jié)點(diǎn)。
  3. 當(dāng)橫向節(jié)點(diǎn)遍歷完成后則向上尋找橫向節(jié)點(diǎn),直至樹(shù)結(jié)構(gòu)全部遍歷完成。

3. 測(cè)試驗(yàn)證

3.1 編寫(xiě)測(cè)試類

@Test
public void test_iterator() { 
    // 數(shù)據(jù)填充
    GroupStructure groupStructure = new GroupStructure("1", "小傅哥");  
    
    // 雇員信息
    groupStructure.add(new Employee("2", "花花", "二級(jí)部門(mén)"));
    groupStructure.add(new Employee("3", "豆包", "二級(jí)部門(mén)"));
    groupStructure.add(new Employee("4", "蹦蹦", "三級(jí)部門(mén)"));
    groupStructure.add(new Employee("5", "大燒", "三級(jí)部門(mén)"));
    groupStructure.add(new Employee("6", "虎哥", "四級(jí)部門(mén)"));
    groupStructure.add(new Employee("7", "玲姐", "四級(jí)部門(mén)"));
    groupStructure.add(new Employee("8", "秋雅", "四級(jí)部門(mén)"));   
    
    // 節(jié)點(diǎn)關(guān)系 1->(1,2) 2->(4,5)
    groupStructure.addLink("1", new Link("1", "2"));
    groupStructure.addLink("1", new Link("1", "3"));
    groupStructure.addLink("2", new Link("2", "4"));
    groupStructure.addLink("2", new Link("2", "5"));
    groupStructure.addLink("5", new Link("5", "6"));
    groupStructure.addLink("5", new Link("5", "7"));
    groupStructure.addLink("5", new Link("5", "8"));       

    Iterator<Employee> iterator = groupStructure.iterator();
    while (iterator.hasNext()) {
        Employee employee = iterator.next();
        logger.info("{},雇員 Id:{} Name:{}", employee.getDesc(), employee.getuId(), employee.getName());
    }
}

3.2 測(cè)試結(jié)果

22:23:37.166 [main] INFO  org.itstack.demo.design.test.ApiTest - 二級(jí)部門(mén),雇員 Id:2 Name:花花
22:23:37.168 [main] INFO  org.itstack.demo.design.test.ApiTest - 三級(jí)部門(mén),雇員 Id:4 Name:蹦蹦
22:23:37.169 [main] INFO  org.itstack.demo.design.test.ApiTest - 三級(jí)部門(mén),雇員 Id:5 Name:大燒
22:23:37.169 [main] INFO  org.itstack.demo.design.test.ApiTest - 四級(jí)部門(mén),雇員 Id:6 Name:虎哥
22:23:37.169 [main] INFO  org.itstack.demo.design.test.ApiTest - 四級(jí)部門(mén),雇員 Id:7 Name:玲姐
22:23:37.169 [main] INFO  org.itstack.demo.design.test.ApiTest - 四級(jí)部門(mén),雇員 Id:8 Name:秋雅
22:23:37.169 [main] INFO  org.itstack.demo.design.test.ApiTest - 二級(jí)部門(mén),雇員 Id:3 Name:豆包

Process finished with exit code 0
  • 從遍歷的結(jié)果可以看到,我們是順著樹(shù)形結(jié)構(gòu)的深度開(kāi)始遍歷,一直到右側(cè)的節(jié)點(diǎn)3雇員 Id:2、雇員 Id:4...雇員 Id:3

六、總結(jié)

  • 迭代器的設(shè)計(jì)模式從以上的功能實(shí)現(xiàn)可以看到,滿足了單一職責(zé)和開(kāi)閉原則,外界的調(diào)用方也不需要知道任何一個(gè)不同的數(shù)據(jù)結(jié)構(gòu)在使用上的遍歷差異??梢苑浅7奖愕臄U(kuò)展,也讓整個(gè)遍歷變得更加干凈整潔。
  • 但從結(jié)構(gòu)的實(shí)現(xiàn)上可以看到,迭代器模式的實(shí)現(xiàn)過(guò)程相對(duì)來(lái)說(shuō)是比較負(fù)責(zé)的,類的實(shí)現(xiàn)上也擴(kuò)增了需要外部定義的類,使得遍歷與原數(shù)據(jù)結(jié)構(gòu)分開(kāi)。雖然這是比較麻煩的,但可以看到在使用java的jdk時(shí)候,迭代器的模式還是很好用的,可以非常方便擴(kuò)展和升級(jí)。
  • 以上的設(shè)計(jì)模式場(chǎng)景實(shí)現(xiàn)過(guò)程可能對(duì)新人有一些不好理解點(diǎn),包括;迭代器三個(gè)和接口的定義、樹(shù)形結(jié)構(gòu)的數(shù)據(jù)關(guān)系、樹(shù)結(jié)構(gòu)深度遍歷思路。這些都需要反復(fù)實(shí)現(xiàn)練習(xí)才能深入的理解,事必躬親,親歷親為,才能讓自己掌握這些知識(shí)。

七、推薦閱讀

  • 1. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)工廠方法模式「多種類型商品不同接口,統(tǒng)一發(fā)獎(jiǎng)服務(wù)搭建場(chǎng)景」
  • 2. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)原型模式「上機(jī)考試多套試,每人題目和答案亂序排列場(chǎng)景」
  • 3. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場(chǎng)景」
  • 4. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)組合模式「營(yíng)銷(xiāo)差異化人群發(fā)券,決策樹(shù)引擎搭建場(chǎng)景」
  • 5. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)外觀模式「基于SpringBoot開(kāi)發(fā)門(mén)面模式中間件,統(tǒng)一控制接口白名單場(chǎng)景」
  • 6. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)享元模式「基于Redis秒殺,提供活動(dòng)與庫(kù)存信息查詢場(chǎng)景」

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多