重學 Java 設計模式:實戰迭代器模式「模擬公司組織架構樹結構關係,深度迭代遍歷人員信息輸出場景」


做者:小傅哥
博客:https://bugstack.cn - 原創系列專題文章html

沉澱、分享、成長,讓本身和他人都能有所收穫!😄

1、前言

相信相信的力量!java

從懵懂的少年,到拿起鍵盤,能夠寫一個HelloWorld。多數人在這並不會感受有多難,也不會認爲作不出來。由於這樣的例子,有老師的指導、有書本的例子、有前人的經驗。但隨着你的開發時間愈來愈長,要解決更復雜的問題或者技術創新,所以在網上搜了幾天幾夜都沒有答案,這個時候是否想過放棄,仍是一直堅持不斷的嘗試一點點完成本身內心要的結果。每每這種沒有前車可鑑須要本身解決問題的時候,可能真的會折磨到要崩潰,但你要願意執着、願意倔強,願意選擇相信相信的力量,就必定能解決。哪怕解決不了,也能夠在這條路上摸索出其餘更多的收穫,爲後續前進的道路填充好墊腳石。設計模式

時間緊是寫垃圾代碼的理由?數組

擰螺絲?Ctrl+C、Ctrl+V?貼膏藥同樣寫代碼?沒有辦法,沒有時間,每每真的是藉口,胸中沒用筆墨,才只能湊合。難道必定是好好寫代碼就浪費時間,拼湊CRUD就快嗎,根本不可能的。由於不會,沒用實操過,不多架構出全場景的設計,才很難寫出優良的代碼。多加強自身的編碼(武術)修爲,在各類編碼場景中讓本身變得老練,纔好應對緊急狀況下的需求開發和人員安排。就像韓信同樣有謀有略,才能執掌百萬雄兵。微信

不要只是作個工具人!數據結構

由於平常的編寫簡單業務需求,致使本身像個工具人同樣,日久天長的也就不多去深刻學習更多技術棧。看見有工具、有組件、有框架,拿來就用用,反正沒什麼體量也不會出什麼問題。但若是你想要更多的收入,哪怕是重複的造輪子,你也要去嘗試造一個,就算不用到生產,本身玩玩總能夠吧。有些事情只有本身經歷過,纔能有最深的感觸,參與過實踐過,纔好總結點評學習。架構

2、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程一個,能夠經過關注公衆號bugstack蟲洞棧,回覆源碼下載獲取(打開獲取的連接,找到序號18)
工程 描述
itstack-demo-design-15-00 開發樹形組織架構關係迭代器

3、迭代器模式介紹

迭代器模式,圖片來自 refactoringguru.cn

迭代器模式,常見的就是咱們平常使用的iterator遍歷。雖然這個設計模式在咱們的實際業務開發中的場景並很少,但卻幾乎天天都要使用jdk爲咱們提供的list集合遍歷。另外加強的for循環雖然是循環輸出數據,可是他不是迭代器模式。迭代器模式的特色是實現Iterable接口,經過next的方式獲取集合元素,同時具有對元素的刪除等操做。而加強的for循環是不能夠的。框架

這種設計模式的優勢是可讓咱們以相同的方式,遍歷不一樣的數據結構元素,這些數據結構包括;數組鏈表等,而用戶在使用遍歷的時候並不須要去關心每一種數據結構的遍歷處理邏輯,從讓使用變得統一易用。工具

4、案例場景模擬

場景模擬;公司樹形組織架構

在本案例中咱們模擬迭代遍歷輸出公司中樹形結構的組織架構關係中僱員列表學習

大部分公司的組織架構都是金字塔結構,也就這種樹形結構,分爲一級、二級、三級等部門,每一個組織部門由僱員填充,最終體現出一個總體的樹形組織架構關係。

通常咱們經常使用的遍歷就是jdk默認提供的方法,對list集合遍歷。可是對於這樣的偏業務特性較大的樹形結構,若是須要使用到遍歷,那麼就能夠本身來實現。接下來咱們會把這個組織層次關係經過樹形數據結構來實現,並完成迭代器功能。

5、迭代器模式遍歷組織結構

在實現迭代器模式以前能夠先閱讀下javalist方法關於iterator的實現部分,幾乎全部的迭代器開發都會按照這個模式來實現,這個模式主要分爲如下幾塊;

  1. Collection,集合方法部分用於對自定義的數據結構添加通用方法;addremoveiterator等核心方法。
  2. Iterable,提供獲取迭代器,這個接口類會被Collection繼承。
  3. Iterator,提供了兩個方法的定義;hasNextnext,會在具體的數據結構中寫實現方式。

除了這樣通用的迭代器實現方式外,咱們的組織關係結構樹,是由節點和節點間的關係鏈構成,因此會比上述的內容多一些入參。

1. 工程結構

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

迭代器模式模型結構

迭代器模式模型結構

  • 以上是咱們工程類圖的模型結構,左側是對迭代器的定義,右側是在數據結構中實現迭代器功能。
  • 關於左側部分的實現與jdk中的方式是同樣的,因此在學習的過程當中能夠互相參考,也能夠本身擴展學習。
  • 另外這個遍歷方式一個樹形結構的深度遍歷,爲了能夠更加讓學習的小夥伴容易理解,這裏我實現了一種比較簡單的樹形結構深度遍歷方式。後續讀者也能夠把遍歷擴展爲橫向遍歷也就是寬度遍歷。

2. 代碼實現

2.1 僱員實體類

/**
 * 僱員
 */
public class Employee {

    private String uId;   // ID
    private String name;  // 姓名
    private String desc;  // 備註
    
    // ...get/set
}
  • 這是一個簡單的僱員類,也就是公司員工的信息類,包括必要的信息;id、姓名、備註。

2.2 樹節點鏈路

/**
 * 樹節點鏈路
 */
public class Link {

    private String fromId; // 僱員ID
    private String toId;   // 僱員ID    
    
    // ...get/set
}
  • 這個類用於描述結構樹中的各個節點之間的關係鏈,也就是A to BB to CB to D,以此描述出一套完整的樹組織結構。

2.3 迭代器定義

public interface Iterator<E> {

    boolean hasNext();

    E next();
    
}
  • 這裏的這個類和javajdk中提供的是同樣的,這樣也方面後續讀者能夠對照listIterator進行源碼學習。
  • 方法描述;hasNext,判斷是否有下一個元素、next,獲取下一個元素。這個在list的遍歷中是常常用到的。

2.4 可迭代接口定義

public interface Iterable<E> {

    Iterator<E> iterator();

}
  • 這個接口中提供了上面迭代器的實現Iterator的獲取,也就是後續在本身的數據結構中須要實現迭代器的功能並交給Iterable,由此讓外部調用方進行獲取使用。

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,同時繼承了另一個接口Iterable的方法iterator()。這樣後續誰來實現這個接口,就須要實現上述定義的一些基本功能;添加元素刪除元素遍歷
  • 同時你可能注意到這裏定義了兩個泛型<E, L>,由於咱們的數據結構一個是用於添加元素,另一個是用於添加樹節點的鏈路關係。

2.6 (核心)迭代器功能實現

public class GroupStructure implements Collection<Employee, Link> {

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

    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);

                // 同級節點掃描
                if (null == links) {
                    cursorIdx = getCursorIdx(fromId);
                    links = linkMap.get(fromId);
                }

                // 上級節點掃描
                while (cursorIdx > links.size() - 1) {
                    fromId = invertedMap.get(fromId);
                    cursorIdx = getCursorIdx(fromId);
                    links = linkMap.get(fromId);
                }

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

                // 返回結果
                return employeeMap.get(link.getToId());
            }
             
            // 給每一個層級定義寬度遍歷進度
            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;
            }
        };
    }

}
  • 以上的這部分代碼稍微有點長,主要包括了對元素的添加和刪除。另外最重要的是對遍歷的實現 new Iterator<Employee>
  • 添加和刪除元素相對來講比較簡單,使用了兩個map數組結構進行定義;僱員列表組織架構關係;id->list。當元素添加元素的時候,會分別在不一樣的方法中向map結構中進行填充指向關係(A->B),也就構建出了咱們的樹形組織關係。

迭代器實現思路

  1. 這裏的樹形結構咱們須要作的是深度遍歷,也就是左側的一直遍歷到最深節點。
  2. 當遍歷到最深節點後,開始遍歷最深節點的橫向節點。
  3. 當橫向節點遍歷完成後則向上尋找橫向節點,直至樹結構所有遍歷完成。

3. 測試驗證

3.1 編寫測試類

@Test
public void test_iterator() { 
    // 數據填充
    GroupStructure groupStructure = new GroupStructure("1", "小傅哥");  
    
    // 僱員信息
    groupStructure.add(new Employee("2", "花花", "二級部門"));
    groupStructure.add(new Employee("3", "豆包", "二級部門"));
    groupStructure.add(new Employee("4", "蹦蹦", "三級部門"));
    groupStructure.add(new Employee("5", "大燒", "三級部門"));
    groupStructure.add(new Employee("6", "虎哥", "四級部門"));
    groupStructure.add(new Employee("7", "玲姐", "四級部門"));
    groupStructure.add(new Employee("8", "秋雅", "四級部門"));   
    
    // 節點關係 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 測試結果

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

Process finished with exit code 0
  • 從遍歷的結果能夠看到,咱們是順着樹形結構的深度開始遍歷,一直到右側的節點3僱員 Id:二、僱員 Id:4...僱員 Id:3

6、總結

  • 迭代器的設計模式從以上的功能實現能夠看到,知足了單一職責和開閉原則,外界的調用方也不須要知道任何一個不一樣的數據結構在使用上的遍歷差別。能夠很是方便的擴展,也讓整個遍歷變得更加乾淨整潔。
  • 但從結構的實現上能夠看到,迭代器模式的實現過程相對來講是比較負責的,類的實現上也擴增了須要外部定義的類,使得遍歷與原數據結構分開。雖然這是比較麻煩的,但能夠看到在使用java的jdk時候,迭代器的模式仍是很好用的,能夠很是方便擴展和升級。
  • 以上的設計模式場景實現過程可能對新人有一些很差理解點,包括;迭代器三個和接口的定義、樹形結構的數據關係、樹結構深度遍歷思路。這些都須要反覆實現練習才能深刻的理解,事必躬親,親歷親爲,才能讓本身掌握這些知識。

7、推薦閱讀

相關文章
相關標籤/搜索