11. 設計模式-組合模式

設計模式-組合模式

1. 案例引出組合模式

編寫程序展示一個學校院系結構(使用數據庫中的表間關係很容易實現,注意這裏是使用Java程序實現):需求是這樣,要在一個頁面中展示出學校的院系組成,一個學校有多個學院,一個學院有多個專業,如下圖:

在這裏插入圖片描述

傳統方式解決上述問題(類圖如下):

在這裏插入圖片描述
傳統方案存在的問題:

  1. 將學院看做是學校的子類, 專業是學院的子類,這樣實際上是站在組織大小來進行分層次的。
  2. 實際上我們的要求是 :在一個頁面中展示出學校的院系組成,一個學校有多個學院,一個學院有多個專業, 因此這種方案, 不能很好實現的管理的操作,比如對學院、專業的 添加,刪除,遍歷等。
  3. 解決方案:把學校、院、系都看做是組織結構,他們之間沒有繼承的關係,而是一個樹形結構,可以更好的實現管理操作。這就要使用到設計模式中的組合模式。

2. 組合模式

2.1 組合模式基本介紹

  1. 組合模式(Composite Pattern),又叫部分整體模式,它創建了對象組的樹形結構,將對象組合成樹狀結構以表示“ 整體- 部分”的層次關係
  2. 組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。
  3. 這種類型的設計模式屬於結構型模式
  4. 組合模式使得 用戶對單個對象和組合對象的訪問具有一致性,即:組合能讓客戶以一致的方式處理個別對象以及組合對象。

2.2 組合模式類圖

  1. Component :這是組合中對象聲明接口,在適當情況下,實現所有類共有的接口默認行爲,用於訪問和管理Component 子部件, Component 可以是抽象類或者接口。
  2. Leaf : 在組合中表示葉子節點,葉子節點沒有子節點。
  3. Composite :非葉子節點, 用於存儲子部件, 在 Component 接口中實現 子部件的相關操作,比如增加(add),刪除。
    在這裏插入圖片描述

2.3 組合模式解決學校院系展示

類圖結構

在這裏插入圖片描述

代碼實現
OrganizationComponent
public abstract class OrganizationComponent {

	private String name; // 名字
	private String des; // 說明
	
	protected  void add(OrganizationComponent organizationComponent) {
		//默認實現
		throw new UnsupportedOperationException();
	}
	
	protected  void remove(OrganizationComponent organizationComponent) {
		//默認實現
		throw new UnsupportedOperationException();
	}

	//構造器
	public OrganizationComponent(String name, String des) {
		super();
		this.name = name;
		this.des = des;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDes() {
		return des;
	}

	public void setDes(String des) {
		this.des = des;
	}
	
	//方法print, 做成抽象的, 子類都需要實現
	protected abstract void print();
	
	
}
University--Composite

//University 就是 Composite , 可以管理College
public class University extends OrganizationComponent {

	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 構造器
	public University(String name, String des) {
		super(name, des);
	}

	// 重寫add
	@Override
	protected void add(OrganizationComponent organizationComponent) {
		organizationComponents.add(organizationComponent);
	}

	// 重寫remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {
		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {
		return super.getName();
	}

	@Override
	public String getDes() {
		return super.getDes();
	}

	// print方法,就是輸出University 包含的學院
	@Override
	protected void print() {
		System.out.println("--------------" + getName() + "--------------");
		//遍歷 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
			organizationComponent.print();
		}
	}

}
College
public class College extends OrganizationComponent {

	//List 中存放的Department
	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 構造器
	public College(String name, String des) {
		super(name, des);
	}

	// 重寫add
	@Override
	protected void add(OrganizationComponent organizationComponent) {
		//  將來實際業務中,Colleage 的 add 和  University add 不一定完全一樣
		organizationComponents.add(organizationComponent);
	}

	// 重寫remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {
		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {
		return super.getName();
	}

	@Override
	public String getDes() {
		return super.getDes();
	}

	// print方法,就是輸出University 包含的學院
	@Override
	protected void print() {
		System.out.println("--------------" + getName() + "--------------");
		//遍歷 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
			organizationComponent.print();
		}
	}


}
Department

public class Department extends OrganizationComponent {

	//沒有集合
	public Department(String name, String des) {
		super(name, des);
	}

	
	//add , remove 就不用寫了,因爲他是葉子節點
	@Override
	public String getName() {
		return super.getName();
	}
	
	@Override
	public String getDes() {
		return super.getDes();
	}
	
	@Override
	protected void print() {
		System.out.println(getName());
	}

}
Client
public class Client {

	public static void main(String[] args) {
		//從大到小創建對象 學校
		OrganizationComponent university = new University("南京工程學院", " 中國頂級大學 ");
		
		//創建 學院
		OrganizationComponent computerCollege = new College("計算機學院", " 計算機學院 ");
		OrganizationComponent infoEngineercollege = new College("信息工程學院", " 信息工程學院 ");
		
		
		//創建各個學院下面的系(專業)
		computerCollege.add(new Department("軟件工程", " 軟件工程不錯 "));
		computerCollege.add(new Department("網絡工程", " 網絡工程不錯 "));
		computerCollege.add(new Department("計算機科學與技術", " 計算機科學與技術是老牌的專業 "));
		
		infoEngineercollege.add(new Department("通信工程", " 通信工程不好學 "));
		infoEngineercollege.add(new Department("信息工程", " 信息工程好學 "));
		
		//將學院加入到 學校
		university.add(computerCollege);
		university.add(infoEngineercollege);
		
		//university.print();
		infoEngineercollege.print();
	}

}

3 .組合模式在 JDK Map集合源碼體現

Java 的集合類HashMap 就使用了組合模式

HashMap中的靜態內部類Node相當於組合模式的中的Leaf 即葉子節點,葉子節點沒有子節點。

Map接口相當於組合模式中的Component對象聲明接口,用於訪問和管理Component 子部件。

HashMap相當於組合模式中的Composite非葉子節點, 用於存儲子部件,具體通過兩個 方法體現出:

putAll

public void putAll(Map<? extends K, ? extends V> m)

    /**
     * 將指定映射中的所有映射覆制到此映射。這些映射將替換此映射對指定映射中當前鍵的任何映射。
     */
    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);
            }
        }
    }
put

public V put(K key, V value) ;

  	/**
     *將此映射中的指定值與指定鍵關聯。如果映射以前包含鍵的映射,舊值將被替換。
     */
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * 實現了Map及put相關方法
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
Node

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
        
        // ...
    }

put方法是將單個Node添加到HashMap中,而putAll方法是將一個HashMap放到另一個HashMap中,通過下面的關係映射:

  1. Node ----> 專業
  2. put ----> 院系添加專業
  3. putAll ----> 學校添加院系

因爲put和putAll都是HashMap中的方法,所以HashMap相當於組合模式中的Composite非葉子節點。這樣下來就可以理解了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章