文章目錄
設計模式-組合模式
1. 案例引出組合模式
編寫程序展示一個學校院系結構(使用數據庫中的表間關係很容易實現,注意這裏是使用Java程序實現):需求是這樣,
要在一個頁面中展示出學校的院系組成,一個學校有多個學院,一個學院有多個專業,
如下圖:傳統方式解決上述問題(類圖如下):
傳統方案存在的問題:
- 將學院看做是學校的子類, 專業是學院的子類,這樣實際上是站在組織大小來進行分層次的。
- 實際上我們的要求是 :在一個頁面中展示出學校的院系組成,一個學校有多個學院,一個學院有多個專業, 因此這種方案, 不能很好實現的管理的操作,比如對學院、專業的 添加,刪除,遍歷等。
- 解決方案:把學校、院、系都看做是組織結構,他們之間沒有繼承的關係,而是一個樹形結構,可以更好的實現管理操作。
這就要使用到設計模式中的組合模式。
2. 組合模式
2.1 組合模式基本介紹
組合模式(Composite Pattern),又叫部分整體模式
,它創建了對象組的樹形結構,將對象組合成樹狀結構以表示“ 整體- 部分”的層次關係
。- 組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。
- 這種類型的設計模式屬於結構型模式。
組合模式使得 用戶對單個對象和組合對象的訪問具有一致性,即:組合能讓客戶以一致的方式處理個別對象以及組合對象。
2.2 組合模式類圖
Component :這是組合中對象聲明接口
,在適當情況下,實現所有類共有的接口默認行爲,用於訪問和管理Component 子部件, Component 可以是抽象類或者接口。Leaf : 在組合中表示葉子節點,葉子節點沒有子節點。
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
中,通過下面的關係映射:
- Node ----> 專業
- put ----> 院系添加專業
putAll
----> 學校添加院系因爲put和
putAll
都是HashMap
中的方法,所以HashMap
相當於組合模式中的Composite
非葉子節點。這樣下來就可以理解了。