題外話
本來是周更的頻率, 因?yàn)檎鎸?shí)的"小光"真實(shí)地逃離了北京, 回了武漢, 回了老家, 處理了一些私人事務(wù). 也就有快一個(gè)月時(shí)間沒有更新了, 抱歉.
年終總結(jié)也都沒有時(shí)間寫, 抽空寫這么一篇, 提前祝大家新年快樂, 過年回家路上平安順利.
前情提要
上集講到, 小光統(tǒng)一了各個(gè)分店的制作熱干面的流程, 并且引入模板方法在不改變熱干面的制作流程基礎(chǔ)上可以定制其中某些步驟. 通過這些措施, 小光熱干面的品牌性也是更加深入人心, 生意紅紅火火.
此時(shí)的小光, 有了更多的時(shí)間去思考公司的前程, 他認(rèn)為, 一個(gè)良好的有序的組織架構(gòu)是公司發(fā)展的必要點(diǎn). 而此時(shí), 小光也有覺得是時(shí)候梳理下公司的組織架構(gòu)了.
所有示例源碼已經(jīng)上傳到Github, 戳這里
組織架構(gòu)
小光參考了當(dāng)前很多公司的一些架構(gòu)方式, 根據(jù)分店, 職責(zé)簡單梳理了下目前小光熱干面的架構(gòu):
整個(gè)就是一個(gè)樹形結(jié)構(gòu).
- 小光熱干面總部下面管理著各個(gè)分店, 也有其自己的部門.
- 各個(gè)分店也有著自己的部門.
設(shè)計(jì)一套系統(tǒng)來表達(dá)這個(gè)組織架構(gòu)
梳理完組織關(guān)系后, 小光覺得有必要設(shè)計(jì)一套系統(tǒng)來管理這個(gè)關(guān)系圖, 以便后續(xù)能方便的查看和管理這個(gè)架構(gòu), 并能很清晰地講解給新員工.
簡單的開始
一開始, 小光并沒有想太多, 按照不同的層級(jí), 分別創(chuàng)建了總公司, 分店, 部門三個(gè)類:
// 部門
public class Department {
private String name;
public Department(String name) {
this.name = name;
}
@Override
public String toString() {
return "部門:" + name;
}
}
// 分店
public class BranchOffice {
private String name;
public BranchOffice(String name) {
this.name = name;
}
private List<Department> departments = new ArrayList<>();
public void add(Department sub) {
departments.add(sub);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("分公司:" + name);
for (Department dept : departments) {
builder.append(" - " + dept.toString());
}
return builder.toString();
}
}
// 總公司
public class HeadOffice {
private String name;
public HeadOffice(String name) {
this.name = name;
}
private List<Department> departments = new ArrayList<>();
public void add(Department sub) {
departments.add(sub);
}
private List<BranchOffice> branches = new ArrayList<>();
public void add(BranchOffice branchOffice) {
branches.add(branchOffice);
}
public void print() {
System.out.println("總公司:" + name);
for (BranchOffice branch : branches) {
System.out.println(" - " + branch);
}
for (Department dept : departments) {
System.out.println(" - " + dept);
}
}
}
利用這些層次的類來組成小光的組織架構(gòu):
public class XiaoGuang {
public static void main(String[] args) {
HeadOffice headOffice = new HeadOffice("小光熱干面");
Department financeDept = new Department("財(cái)務(wù)部");
Department strategyDept = new Department("策劃部");
BranchOffice ov = new BranchOffice("光谷分店");
ov.add(financeDept);
ov.add(strategyDept);
BranchOffice huashan = new BranchOffice("花山分店");
huashan.add(financeDept);
huashan.add(strategyDept);
headOffice.add(financeDept);
headOffice.add(strategyDept);
headOffice.add(ov);
headOffice.add(huashan);
headOffice.print();
}
}
結(jié)果如下, 達(dá)到要求:
總公司:小光熱干面
- 分公司:光谷分店 - 部門:財(cái)務(wù)部 - 部門:策劃部
- 分公司:花山分店 - 部門:財(cái)務(wù)部 - 部門:策劃部
- 部門:財(cái)務(wù)部
- 部門:策劃部
小光的思考
看著這新鮮出爐的架構(gòu)程序, 小光總覺得哪兒不對(duì)勁兒. 畢竟小光是歷經(jīng)了北上廣各種類型公司的人才啊, 回想起上班敲代碼的日子, 小光想起了哪兒不妥了. 公司總會(huì)發(fā)展, 發(fā)展過程中總會(huì)有一些戰(zhàn)略調(diào)整, 從而導(dǎo)致公司部門的各種變化.
目前小光設(shè)計(jì)的組織架構(gòu)是三層架構(gòu), 但是隨著公司的發(fā)展壯大, 很有可能層級(jí)會(huì)變得更多. 比如說, 總部的采購部壯大了, 可能會(huì)增加下一級(jí)的食材采購部和設(shè)備采購部. 甚至可能現(xiàn)在的層級(jí)還會(huì)隨著公司的戰(zhàn)略調(diào)整而升降. 例如, 如果分店開到別的城市了, 可能會(huì)在總部和分店之間插入一層"子公司"來分別管理各地的分店. 如下:
那么就出現(xiàn)了一個(gè)我們一直在強(qiáng)調(diào)的問題: 有些東西可能會(huì)一直有變化, 而我們從產(chǎn)品角度肯定會(huì)想要擁抱變化, 然而代碼層面上我們又不想修改代碼(修改原有代碼意味著還有對(duì)原有邏輯負(fù)責(zé)).
那應(yīng)該怎么辦呢? 有沒有更好的方式來表達(dá)這種樹形的組織架構(gòu)關(guān)系呢? 以便能夠很容易地?cái)U(kuò)展層次呢?
小光又回想起了自己的碼農(nóng)時(shí)代, 回想當(dāng)年這種問題的解決方案:
既然是因?yàn)樽兓? 擴(kuò)展而引起的問題, 我們最重要是要先找出系統(tǒng)中可變和不可變的部分, 封裝不可變(使其修改關(guān)閉), 擁抱可變(使其擴(kuò)展開放).
小光的解決之道
那么具體到這個(gè)組織系統(tǒng)架構(gòu)的問題, 又該怎么做呢?
小光可不想每增加一個(gè)層級(jí)都要重新為其增加一個(gè)類, 然后改變原有系統(tǒng). 特別是有些情況下, 曾加的類(子公司)中可能會(huì)包含分店和自己的部門. 隨著類別的增加, 每次增加一個(gè)層級(jí)將會(huì)越來越難.
小光想到著, 突然想到:
每個(gè)部門/分店/子公司乃至總公司不都是部門的集合嗎?
部門可能有下屬部門, 可能沒有
分店下有部門
子公司下有分店, 可能還有自己的部門
總公司下有子公司, 分店, 自己的部門
所以說, 所有實(shí)體(總公司/子公司/分店/部門)實(shí)際上都是由部門組成的. 這些實(shí)體也都可以看著是一個(gè)部門.
想到做到:
首先定義出基礎(chǔ)的部門:
public class Department {
private String name;
public Department(String name) {
this.name = name;
}
private List<Department> subDepartments = new ArrayList<>();
public void add(Department sub) {
subDepartments.add(sub);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(name);
for (Department dept : subDepartments) {
builder.append("\n");
builder.append(" - " + dept.toString());
}
return builder.toString();
}
}
來看下, 利用Department這一個(gè)類別怎么搭建組織:
public class XiaoGuang {
public static void main(String[] args) {
Department financeDept = new Department("財(cái)務(wù)部");
Department strategyDept = new Department("策劃部");
Department ovBranchOffice = new Department("光谷分店");
ovBranchOffice.add(financeDept);
ovBranchOffice.add(strategyDept);
Department huashanBranchOffice = new Department("花山分店");
huashanBranchOffice.add(financeDept);
huashanBranchOffice.add(strategyDept);
Department wuhanChildOffice = new Department("武漢子公司");
wuhanChildOffice.add(ovBranchOffice);
wuhanChildOffice.add(huashanBranchOffice);
Department changshaChildOffice = new Department("長沙子公司");
Department xiaoguang = new Department("小光熱干面");
xiaoguang.add(wuhanChildOffice);
xiaoguang.add(changshaChildOffice);
xiaoguang.add(financeDept);
xiaoguang.add(strategyDept);
System.out.println(xiaoguang);
}
}
輸出如下
小光熱干面
- 武漢子公司
- 光谷分店
- 財(cái)務(wù)部
- 策劃部
- 花山分店
- 財(cái)務(wù)部
- 策劃部
- 長沙子公司
- 財(cái)務(wù)部
- 策劃部
輸出稍有調(diào)整, 以顯示直觀的層級(jí). 大家可以自行修改下代碼, 來給department加入level屬性, 以便輸出更完美的結(jié)構(gòu).
到此, 我們算是解決了組織架構(gòu)的問題了, 以后不管是增加了什么層級(jí), 我們都只需要使用Department來表示即可, 而無需增加一個(gè)類了.
故事之后
照例, 我們來縷縷小光設(shè)計(jì)的這套系統(tǒng)的"類關(guān)系":
結(jié)構(gòu)相當(dāng)簡單, 實(shí)際上就是一個(gè)類, 其中關(guān)鍵的是: 這個(gè)類中有一個(gè)list屬性(subDepartments)包含的是一組該類. 有點(diǎn)遞歸的感覺.
這個(gè)就是我們今天想說的 --- 組合模式
組合模式
又叫部分整體模式玄窝,通過組合的方式, 創(chuàng)建一個(gè)包含了一組自己的對(duì)象組(List<Department>)的類(Department). 從而達(dá)成了用一個(gè)類來遞歸地表示一個(gè)整體.組合模式通常用來解決樹形結(jié)構(gòu)的表達(dá)問題, 例如本例中的組織結(jié)構(gòu).
所謂組合的方式, 就是創(chuàng)建一個(gè)包含了一組自己的對(duì)象組的類
注: 我們這里實(shí)現(xiàn)的相對(duì)簡單, 旨在說明模式的形式.
實(shí)際場景中, Department可能是一個(gè)抽象的, 例如有一個(gè)抽象方法來執(zhí)行該部門的職責(zé), 有不同的具體部門實(shí)現(xiàn)來實(shí)現(xiàn)其職責(zé). 從而有不同的"Department實(shí)現(xiàn)"來組合另一個(gè)大的Department節(jié)點(diǎn).
擴(kuò)展閱讀一
前面說到, 組合模式通常用來處理樹形結(jié)構(gòu)的表達(dá)問題. 而我們的用戶界面實(shí)現(xiàn)通常就是一個(gè)UI元素的節(jié)點(diǎn)樹. 例如HTML中的div中可能有button, 還會(huì)有其他div層級(jí), 依次往下; Java SE中的界面實(shí)現(xiàn)AWT和Swing包也到處是組合模式的體現(xiàn); Android中的View體系, View和ViewGroup, Layout層級(jí)樹也都是組合模式的體現(xiàn).
在此我們以Android的View體系為例, 簡單描述下.
眾所周知, Android的界面實(shí)際上就是一顆大樹, Hierarchy Viewer中展示的就是一個(gè)樹形結(jié)構(gòu). 我們來看下Android是怎么利用組合模式實(shí)現(xiàn)的.
如下:
- ViewGroup就是相當(dāng)于例子中的Department, 這里面比我們的例子復(fù)雜的是, ViewGroup是擴(kuò)展自View的, View實(shí)際上我們可以理解為是葉子節(jié)點(diǎn), 也就是最小的組成單元了, 不再有以下的層級(jí)了.
- ViewGroup中包含了一組View對(duì)象, 因?yàn)閂iewGroup是View的子類, 故而我們也可以說ViewGroup中包含了一組ViewGroup和/或View. 進(jìn)入形成了我們之前說的遞歸, 生成一個(gè)樹形結(jié)構(gòu).
關(guān)于View的體系, 在此不細(xì)述了.
搭建好彈性可擴(kuò)展的組織架構(gòu)體系, 小光又開始將眼光轉(zhuǎn)移到市場, 轉(zhuǎn)移到業(yè)務(wù)了, 準(zhǔn)備著新年大展拳腳~