欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java設(shè)計(jì)模式之組合模式

 更新時(shí)間:2022年10月11日 09:07:47   作者:tianClassmate  
這篇文章介紹了Java設(shè)計(jì)模式之組合模式,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

本文通過老王和小王探討書房、書架、各類書的管理問題,引出結(jié)構(gòu)型設(shè)計(jì)模式家族中的一個(gè)重要成員——組合模式,本文會給予兩種組合模式的典型代碼實(shí)現(xiàn),為了加深理解會在第三部分應(yīng)用中介紹組合模式在源碼中的實(shí)際運(yùn)用,最后總結(jié)該設(shè)計(jì)模式學(xué)習(xí)后的一些思考。

讀者可以拉取完整代碼到本地進(jìn)行學(xué)習(xí),實(shí)現(xiàn)代碼均測試通過后上傳到碼云,本地源碼下載。

一、引出問題

上篇文章中老王給小王買車以后,小王對老王感激涕零,看著老王凌亂的書房,小王提出要幫助老王整理整理他的書架。

小王開始了他的分析。老王平時(shí)博覽群書,中文、英文、梵文...每個(gè)語種占滿了書架,而每個(gè)語種中又分經(jīng)濟(jì)學(xué)、計(jì)算機(jī)學(xué)、社會學(xué)等等類目。這是典型的分層次結(jié)構(gòu),將語種比作是圖書的子類,類目是語種的子類結(jié)構(gòu)劃分。

將圖書、語種、類目都看做是組織結(jié)構(gòu),他們之間沒有繼承關(guān)系,而是一個(gè)樹形結(jié)構(gòu),可以更好的實(shí)現(xiàn)管理操作。

二、概念與使用

實(shí)際上,小王提出來的設(shè)計(jì)思路正是結(jié)構(gòu)型設(shè)計(jì)模式中的組合模式,我們首先看一下組合模式的相關(guān)概念,組合模式(Composite Pattern),又叫部分整體模式,它創(chuàng)建了對象組的樹形結(jié)構(gòu),將對象組合成樹狀結(jié)構(gòu)以表示“整體-部分”的層次關(guān)系。組合模式依據(jù)樹形結(jié)構(gòu)來組合對象,用來表示部分以及整體層次。

組合模式使得用戶對單個(gè)對象和組合對象的訪問具有一致性,即:組合能讓客戶以一致的方式處理個(gè)別對象以及組合對象。

用大白話解釋也就是,在實(shí)際應(yīng)用中將所有圖書依照樹形模式進(jìn)行組合,老王尋找書籍時(shí),無論是訪問某一類書還是某一個(gè)語種的書,使用同樣的姿勢即可,保證了訪問的一致性。

在該模式中應(yīng)該是有三個(gè)角色:

1、Root :這是組合中對象聲明接口,在適當(dāng)情況下,實(shí)現(xiàn)所有類共有的接口默認(rèn)行為,用于訪問和管理Root 子部件, Root 可以是抽象類或者接口。

2、Branches:非葉子節(jié)點(diǎn)用于存儲子部件,在Root接口中實(shí)現(xiàn)了 子部件的相關(guān)操作。

2、Leaf : 在組合中表示葉子節(jié)點(diǎn),葉子節(jié)點(diǎn)沒有子節(jié)點(diǎn)。

小王分析的頭頭是道,老王提出來了他的疑問。

當(dāng)我按語種查找還是按類目查找是使用的方法有時(shí)候是不一樣的,如果你把所有方法都定義在Root中,在語種或者類目中實(shí)現(xiàn)中是無意義的,而且這違背了接口隔離原則。

小王覺得說的對也不對,如果我改成不在Root中定義,那么我在客戶端調(diào)用的時(shí)候就需要判斷是枝還是葉了,增加了繁雜的邏輯判斷,而且相比另外一種變得不透明了,依賴倒置原則也沒有遵守。

兩種方式似乎都有缺陷,小王陷入了糾結(jié)不知道該如何取舍,老王提出了他的一些見解,沒有任何一個(gè)設(shè)計(jì)模式是完全沒有缺點(diǎn)的,兩種都有各自的好處,在實(shí)際的運(yùn)用中根據(jù)條件進(jìn)行取舍,而正確選擇的前提就是要對所有的設(shè)計(jì)模式充分的把握。

上面兩種就對應(yīng)組合模式中的兩個(gè)大分類、①透明組合模式、安全組合模式。

①透明組合模式把所有的公共方法都定義在Root中,這樣做的好處就是客戶端無需分辨是葉子節(jié)點(diǎn)(Leaf)和樹枝節(jié)點(diǎn)(Branches),他們具備完全一致的接口;缺點(diǎn)是葉子節(jié)點(diǎn)(Leaf)會繼承得到一些它所不需要(管理子類操作的方法)的方法,這與設(shè)計(jì)模式接口隔離原則相違背。

②安全組合模式的好處是接口定義職責(zé)清晰,符合設(shè)計(jì)模式單一職責(zé)原則和接口隔離原則;缺點(diǎn)是客戶需要區(qū)分樹枝節(jié)點(diǎn)(Branches)和葉子節(jié)點(diǎn)(Leaf),這樣才能正確處理各個(gè)層次的操作,客戶端依賴抽象(Root),違背了依賴倒置原則。

我們把兩種的方式實(shí)現(xiàn),讀者對比他們之間的區(qū)別。

安全模式

Root(根節(jié)點(diǎn)):

/**
 * @author tcy
 * @Date 08-08-2022
 */
public abstract class RootBook {
    protected String name;

    public RootBook(String name) {
        this.name = name;
    }

    public abstract String operation();

}

Branches(樹枝節(jié)點(diǎn))

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class BranchesLanguages extends RootBook {

    private List<RootBook> roots;

    public BranchesLanguages(String name) {
        super(name);
        this.roots = new ArrayList<RootBook>();
    }


    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (RootBook component : this.roots) {
            builder.append("\n");
            builder.append(component.operation());
        }
        return builder.toString();

    }
    public boolean addChild(RootBook component) {
        return this.roots.add(component);
    }


    public boolean removeChild(RootBook component) {
        return this.roots.remove(component);
    }


    public RootBook getChild(int index) {
        return this.roots.get(index);
    }

}

Leaf(葉子節(jié)點(diǎn))

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class LeafClassify extends RootBook {

    public LeafClassify(String name) {
        super(name);
    }

    @Override
    public String operation() {
        return this.name;
    }
}

客戶端:

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class Client {

    public static void main(String[] args) {
        System.out.println("安全組合模式...");
        // 來一個(gè)根節(jié)點(diǎn)
        BranchesLanguages BranchesRoot = new BranchesLanguages("root/書");
        // 來一個(gè)樹枝節(jié)點(diǎn)
        BranchesLanguages branchA = new BranchesLanguages("------branchA/英語");
        BranchesLanguages branchB = new BranchesLanguages("------branchB/中文");
        // 來一個(gè)葉子節(jié)點(diǎn)
        RootBook leafA = new LeafClassify("------leafA/經(jīng)濟(jì)學(xué)");
        RootBook leafB = new LeafClassify("------leafB/計(jì)算機(jī)學(xué)");
        RootBook leafC = new LeafClassify("------leafC/法學(xué)");

        BranchesRoot.addChild(branchA);
        BranchesRoot.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = BranchesRoot.operation();
        System.out.println(result);


    }
}

透明模式

Root(根節(jié)點(diǎn)):

/**
 * @author tcy
 * @Date 08-08-2022
 */
public abstract class RootBook {
    protected String name;

    public RootBook(String name) {
        this.name = name;
    }

    public abstract String operation();

    public boolean addChild(RootBook component) {
        throw new UnsupportedOperationException("addChild not supported!");
    }

    public boolean removeChild(RootBook component) {
        throw new UnsupportedOperationException("removeChild not supported!");
    }

    public RootBook getChild(int index) {
        throw new UnsupportedOperationException("getChild not supported!");
    }

}

Branches(樹枝節(jié)點(diǎn))

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class BranchesLanguages extends RootBook {

    private List<RootBook> roots;

    public BranchesLanguages(String name) {
        super(name);
        this.roots = new ArrayList<RootBook>();
    }


    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (RootBook component : this.roots) {
            builder.append("\n");
            builder.append(component.operation());
        }
        return builder.toString();

    }

    @Override
    public boolean addChild(RootBook component) {
        return this.roots.add(component);
    }


    @Override
    public boolean removeChild(RootBook component) {
        return this.roots.remove(component);
    }


    @Override
    public RootBook getChild(int index) {
        return this.roots.get(index);
    }

}

Leaf(葉子節(jié)點(diǎn))

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class LeafClassify extends RootBook {

    public LeafClassify(String name) {
        super(name);
    }

    @Override
    public String operation() {
        return this.name;
    }
}

客戶端:

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class Client {

    public static void main(String[] args) {

        System.out.println("透明組合模式...");
        // 來一個(gè)根節(jié)點(diǎn)
        RootBook BranchesRoot = new BranchesLanguages("root/書");
        // 來一個(gè)樹枝節(jié)點(diǎn)
        RootBook branchA = new BranchesLanguages("------branchA/英語");
        RootBook branchB = new BranchesLanguages("------branchB/漢語");
        // 來一個(gè)葉子節(jié)點(diǎn)
        RootBook leafA = new LeafClassify("------leafA/計(jì)算機(jī)學(xué)");
        RootBook leafB = new LeafClassify("------leafB/法學(xué)");
        RootBook leafC = new LeafClassify("------leafC/社會學(xué)");

        BranchesRoot.addChild(branchA);
        BranchesRoot.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = BranchesRoot.operation();
        System.out.println(result);


    }
}

使用組合模式的兩種實(shí)現(xiàn)方法,這樣就對老王的書架改造工程就完成了,對憑空捏造出來的需求有些讀者看完想必還是云里霧里。我們結(jié)合JDK的源碼和一些開發(fā)常用框架,再次深入源碼對組合模式的使用。

三、應(yīng)用

通過查詢資料可知,組合模式在Jdk中的應(yīng)用主要是集合類HashMap和Mybtis中的SqlNode。

我們分別看其實(shí)現(xiàn)。

1、jdk中HashMap的運(yùn)用

在HashMap中有一個(gè)父類AbstractMap和一個(gè)子類Node。如下圖

我們看下源代碼:

 public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    ...
     public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    ...
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
    ...
}

putAll()方法傳入的是Map對象,Map就是一個(gè)抽象構(gòu)件(同時(shí)這個(gè)構(gòu)件中只支持健值對的存儲格式),而HashMap是一個(gè)中間構(gòu)件,HashMap中的Node節(jié)點(diǎn)就是葉子節(jié)點(diǎn)。

Node是HashMap中的一個(gè)內(nèi)部類,HashMap的存儲節(jié)點(diǎn)指的正是Node,讀者可以重點(diǎn)看這個(gè)類的實(shí)現(xiàn)。

在這個(gè)實(shí)例中,HashMap就是樹枝節(jié)點(diǎn),Node就是葉節(jié)點(diǎn),Map就是根節(jié)點(diǎn)。

2、Mybtis中的SqlNode

SqlNode是一個(gè)接口,主要功能就是構(gòu)造SQL語句。

public interface SqlNode {
  boolean apply(DynamicContext context);
}

SqlNode有一大堆的實(shí)現(xiàn)類,我們看其中的MixedSqlNode。

  public class MixedSqlNode implements SqlNode {
  private final List<SqlNode> contents;

  public MixedSqlNode(List<SqlNode> contents) {
    this.contents = contents;
  }

  @Override
  public boolean apply(DynamicContext context) {
    contents.forEach(node -> node.apply(context));
    return true;
  }
}

SqlNode就充當(dāng)組合模式中的Root,而他的眾多子類作用就在于拼接各種類型的SQL,在組合模式的角色中相當(dāng)于樹枝節(jié)點(diǎn)。其中在TrimSqlNode 中有一個(gè)子類WhereSqlNode就充當(dāng)組合模式中的樹葉節(jié)點(diǎn)。

這兩個(gè)都屬于組合模式中的典型例子,讀者體會下使用這種模式的好處,和如果不使用組合模式應(yīng)該怎樣實(shí)現(xiàn)。

通過這兩個(gè)例子我們應(yīng)該可以看到,設(shè)計(jì)模式的使用中并不是完全遵循各自的角色,更多的是設(shè)計(jì)模式中的一些變種,讀者不深入源碼并不能了解到該模式的實(shí)現(xiàn)細(xì)節(jié)。讀者需要做的就是盡可能的熟悉設(shè)計(jì)模式,在自己開發(fā)過程中可以“擇優(yōu)錄取”。

四、總結(jié)

到這里組合模式也就介紹完了,這種模式的優(yōu)缺點(diǎn)都非常的明顯,優(yōu)點(diǎn)就在于清楚的定義分層次的結(jié)構(gòu),在調(diào)用時(shí)忽略他們之間的差異,方便對整個(gè)層次進(jìn)行控制,但是組合模式會違反依賴倒置原則。

理解是一回事,在實(shí)際應(yīng)用中能正確的使用它就是另外一回事了。

讀者要對每種設(shè)計(jì)模式都能做到心中有數(shù),當(dāng)我們在實(shí)際編程中,在潛意識里有各個(gè)設(shè)計(jì)模式的大體輪廓,參考代入進(jìn)各種設(shè)計(jì)模式中,對于簡化開發(fā)和易于維護(hù)性有沒有好的幫助,選擇一個(gè)最優(yōu)的設(shè)計(jì)模式。

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接

相關(guān)文章

最新評論