當(dāng)曾經(jīng)的孩子們慢慢步入社會才知道,那年味漸淡的春節(jié)就像是疾馳在人生路上的暫停鍵。
它允許你在隆隆的鞭炮聲中靜下心來,瞻前顧后,悵然若失。
也允許你在寂靜的街道上屏氣凝神,傾聽自己胸腔里的那團人聲鼎沸。
孩子們會明白的,就像他們步入大學(xué)校園時候漸漸明白家鄉(xiāng)只有冬夏,再無春秋一樣。
人生這場旅途,就是無數(shù)個后知后覺的組合。提前看到一些東西不會讓我們清醒半分,相反,這一切反而容易讓人愈發(fā)的沉溺于那來勢洶洶的紙醉金迷…
正月初九,對于多數(shù)打工人來說是真正意義上新的一年的開始,就像是一條條沉睡的金魚被人從湖底撈起再次丟進那個金碧輝煌的魚缸,開始在整個體系內(nèi)扮演不同的角色。組織架構(gòu)、行政管理、經(jīng)理、開發(fā)、部署、運維、測試…
今天,我們不妨就著組織架構(gòu)這個話題,來拆解下結(jié)構(gòu)型設(shè)計模式中將聚合思路用到極致的實現(xiàn)方案:組合模式。
一言
組合模式通過創(chuàng)建對象組的的樹形結(jié)構(gòu),讓客戶以一致的方式處理個別對象以及組合對象。
當(dāng)組織開始優(yōu)化
概念和思路是為需求服務(wù)的,就比如:
我創(chuàng)辦了一家名為“Wayne實業(yè)”的集團公司,為了集團戰(zhàn)略的更好落實,我對組織結(jié)構(gòu)進行了優(yōu)化。集團公司下設(shè)多個省級分公司,每個省級分公司下設(shè)地市級辦事處。
現(xiàn)在我要求你在集團公司網(wǎng)頁上級聯(lián)的展示架構(gòu)信息,有什么思路?
探路式思考
首先明確下,我們不是在具體討論用Vue或React等成熟前端框架的某個組件更優(yōu),而是從實體設(shè)計的角度去思考。
相信很多朋友最先想到的就是繼承關(guān)系,分公司作為集團公司的子類,辦事處作為分公司的子類。誠然,需求的描述實在是太契合繼承關(guān)系的特征了。
但是我們仔細(xì)分析下需求就會發(fā)現(xiàn),這種設(shè)計方式實際上存在很大的問題。集團公司有多個分公司,每個分公司下又有大量的辦事處。這種設(shè)計方式非常不利于對分公司、辦事處的管理??梢运伎枷拢坏┬枰霰闅v或增刪操作要投入多大的成本?
組合模式的思考
其實,組合模式非常善于解決這樣的問題。當(dāng)我們要處理的對象可以憑借需求進化成一棵樹的時候,這一切反而變得簡單。
相信很多朋友讀到這里會有疑問,樹結(jié)構(gòu)在編程中其實并不是一個容易處理的結(jié)構(gòu),為什么還說它簡單?
這是因為在組合模式下,我們對樹上的節(jié)點或者葉子的操作都是扁平的,根本不用考慮它是節(jié)點還是葉子。
就好像一個立體的三維空間突然坍縮成一個平面,我們需要關(guān)注的東西突然少了一個維度,簡單的幸福由此蔓延開來。
設(shè)計
代碼實現(xiàn)
核心
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();
}
protected abstract void print();
public OrganizationComponent(String name, String des) {
this.name = name;
this.des = des;
}
//setter&getter
}
集團公司
public class WayneIndustries extends OrganizationComponent{
List<OrganizationComponent> organizationComponents = new ArrayList<>();
public WayneIndustries(String name, String des) {
super(name, des);
}
@Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println("--------------------"+getName()+"----------------------");
for (OrganizationComponent ogc : organizationComponents)
ogc.print();
}
}
省公司
public class ProvincialCompany extends OrganizationComponent{
List<OrganizationComponent> organizationComponents = new ArrayList<>();
public ProvincialCompany(String name, String des) {
super(name, des);
}
@Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println("--------------------"+getName()+"----------------------");
for (OrganizationComponent ogc : organizationComponents)
ogc.print();
}
}
辦事處
public class Office extends OrganizationComponent{
public Office(String name, String des) {
super(name, des);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println(getName());
}
}
客戶端
public class Client {
public static void main(String[] args) {
OrganizationComponent wayne = new WayneIndustries("Wayne實業(yè)", "世界500強");
OrganizationComponent company1 = new ProvincialCompany("江蘇分公司", "綜合貿(mào)易");
OrganizationComponent company2 = new ProvincialCompany("廣東分公司", "對外貿(mào)易");
company1.add(new Office("徐州辦事處", "主營冶金"));
company1.add(new Office("連云港辦事處", "海港事宜"));
company1.add(new Office("鎮(zhèn)江辦事處", "旅游業(yè)"));
company2.add(new Office("深圳辦事處","對外貿(mào)易"));
company2.add(new Office("珠海辦事處","對澳貿(mào)易"));
wayne.add(company1);
wayne.add(company2);
wayne.print();
// company1.print();
// company2.print();
}
}
測試
組合模式在JDK源碼中的應(yīng)用
筆者在學(xué)習(xí)設(shè)計模式的過程中,有一個很直觀的感受就是:設(shè)計模式給了普通開發(fā)者另一個視角來審視問題,也使得我們看待某些問題變得更靈活。比如頂層抽象,它可以依托于接口也可以依托于抽象類,再比如泛化關(guān)系,它可以依托于繼承和實現(xiàn),也可以依托于靜態(tài)內(nèi)部類。
這種思維方式的提升就像一個兢兢業(yè)業(yè)在工地上碼磚的工人突然間抬頭打量起整個房間的布局甚至整棟大廈的結(jié)構(gòu)原理,那種豁然開朗的感覺或許只有親歷者才能感同身受。
在JDK源碼中,組合模式也十分常見。比如我們熟知的HashMap:
Map<Integer,String> hashMap = new HashMap<>();
hashMap.put(0,"江蘇");
Map<Integer,String> map = new HashMap<>();
map.put(1,"浙江");
map.put(2,"廣東");
hashMap.putAll(map);
System.out.println(hashMap);
在HashMap中,既可以用put加入一個鍵值對,也可以用putAll加入多個鍵值對。
我們通過審視HashMap的結(jié)構(gòu)可以發(fā)現(xiàn)它為了擴展性不但繼承了抽象AbstractMap的同時也實現(xiàn)了Map接口,這兩個抽象實際上都可以看作是組合模式的OrganizationComponent(也就是我之前例子中的集團公司)。而有讀過HashMap源碼的朋友應(yīng)該知道,在HashMap中有個及其關(guān)鍵的靜態(tài)內(nèi)部類Node。
相關(guān)源碼片:
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
它是組成鏈表/紅黑樹的原子結(jié)構(gòu)(這里與本節(jié)無關(guān)暫不做展開,感興趣的朋友可以再查閱下相關(guān)資料),hashMap的put方法、putAll方法都以Node為最小執(zhí)行單位。這樣來看,靜態(tài)內(nèi)部類Node更像是由HashMap泛化來的另一個實現(xiàn)形式(類似于我之前例子中的省公司、辦事處)。這種葉子節(jié)點(Node)與子節(jié)點(HashMap)同根同源(Map),與組合模式理念“不謀而合”。
相關(guān)源碼片:
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
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;
}
/**
* Copies all of the mappings from the specified map to this map.
* These mappings will replace any mappings that this map had for
* any of the keys currently in the specified map.
*
* @param m mappings to be stored in this map
* @throws NullPointerException if the specified map is null
*/
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}
/**
* Implements Map.putAll and Map constructor.
*
* @param m the map
* @param evict false when initially constructing this map, else
* true (relayed to method afterNodeInsertion).
*/
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);
}
}
}
結(jié)
組合模式依據(jù)樹形結(jié)構(gòu)來組合對象,從部分到整體層次分明,是典型的結(jié)構(gòu)型模式。組合模式對用戶使用的單個對象表現(xiàn)出極強的一致性,對客戶而言無需特別關(guān)注個別對象和組合對象。
希望此文能夠讓大家對組合模式有更進一步的理解。文章來源:http://www.zghlxwxcb.cn/news/detail-833954.html
關(guān)注我,共同進步,每周至少一更?!猈ayne文章來源地址http://www.zghlxwxcb.cn/news/detail-833954.html
到了這里,關(guān)于15.一種坍縮式的簡單——組合模式詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!