題外話
本來是周更的頻率, 因為真實的”小光”真實地逃離了北京, 回了武漢, 回了老家, 處理了一些私人事務. 也就有快一個月時間沒有更新了, 抱歉.
年終總結也都沒有時間寫, 抽空寫這麼一篇, 提前祝大家新年快樂, 過年回家路上平安順利.
前情提要
上集講到, 小光統一了各個分店的製作熱乾麵的流程, 並且引入模板方法在不改變熱乾麵的製作流程基礎上可以定製其中某些步驟. 通過這些措施, 小光熱乾麵的品牌性也是更加深入人心, 生意紅紅火火.
此時的小光, 有了更多的時間去思考公司的前程, 他認為, 一個良好的有序的組織架構是公司發展的必要點. 而此時, 小光也有覺得是時候梳理下公司的組織架構了.
所有示例原始碼已經上傳到Github, 戳這裡
組織架構
小光參考了當前很多公司的一些架構方式, 根據分店, 職責簡單梳理了下目前小光熱乾麵的架構:
整個就是一個樹形結構.
- 小光熱乾麵總部下面管理著各個分店, 也有其自己的部門.
- 各個分店也有著自己的部門.
設計一套系統來表達這個組織架構
梳理完組織關係後, 小光覺得有必要設計一套系統來管理這個關係圖, 以便後續能方便的檢視和管理這個架構, 並能很清晰地講解給新員工.
簡單的開始
一開始, 小光並沒有想太多, 按照不同的層級, 分別建立了總公司, 分店, 部門三個類:
// 部門
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);
}
}
}複製程式碼
利用這些層次的類來組成小光的組織架構:
public class XiaoGuang {
public static void main(String[] args) {
HeadOffice headOffice = new HeadOffice("小光熱乾麵");
Department financeDept = new Department("財務部");
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();
}
}複製程式碼
結果如下, 達到要求:
總公司:小光熱乾麵
- 分公司:光谷分店 - 部門:財務部 - 部門:策劃部
- 分公司:花山分店 - 部門:財務部 - 部門:策劃部
- 部門:財務部
- 部門:策劃部複製程式碼
小光的思考
看著這新鮮出爐的架構程式, 小光總覺得哪兒不對勁兒. 畢竟小光是歷經了北上廣各種型別公司的人才啊, 回想起上班敲程式碼的日子, 小光想起了哪兒不妥了. 公司總會發展, 發展過程中總會有一些戰略調整, 從而導致公司部門的各種變化.
目前小光設計的組織架構是三層架構, 但是隨著公司的發展壯大, 很有可能層級會變得更多. 比如說, 總部的採購部壯大了, 可能會增加下一級的食材採購部和裝置採購部. 甚至可能現在的層級還會隨著公司的戰略調整而升降. 例如, 如果分店開到別的城市了, 可能會在總部和分店之間插入一層”子公司”來分別管理各地的分店. 如下:
那麼就出現了一個我們一直在強調的問題: 有些東西可能會一直有變化, 而我們從產品角度肯定會想要擁抱變化, 然而程式碼層面上我們又不想修改程式碼(修改原有程式碼意味著還有對原有邏輯負責).
那應該怎麼辦呢? 有沒有更好的方式來表達這種樹形的組織架構關係呢? 以便能夠很容易地擴充套件層次呢?
小光又回想起了自己的碼農時代, 回想當年這種問題的解決方案:
既然是因為變化, 擴充套件而引起的問題, 我們最重要是要先找出系統中可變和不可變的部分, 封裝不可變(使其修改關閉), 擁抱可變(使其擴充套件開放).
小光的解決之道
那麼具體到這個組織系統架構的問題, 又該怎麼做呢?
小光可不想每增加一個層級都要重新為其增加一個類, 然後改變原有系統. 特別是有些情況下, 曾加的類(子公司)中可能會包含分店和自己的部門. 隨著類別的增加, 每次增加一個層級將會越來越難.
小光想到著, 突然想到:
每個部門/分店/子公司乃至總公司不都是部門的集合嗎?
部門可能有下屬部門, 可能沒有
分店下有部門
子公司下有分店, 可能還有自己的部門
總公司下有子公司, 分店, 自己的部門
所以說, 所有實體(總公司/子公司/分店/部門)實際上都是由部門組成的. 這些實體也都可以看著是一個部門.
想到做到:
首先定義出基礎的部門:
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("
");
builder.append(" - " + dept.toString());
}
return builder.toString();
}
}複製程式碼
來看下, 利用Department這一個類別怎麼搭建組織:
public class XiaoGuang {
public static void main(String[] args) {
Department financeDept = new Department("財務部");
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);
}
}複製程式碼
輸出如下
小光熱乾麵
- 武漢子公司
- 光谷分店
- 財務部
- 策劃部
- 花山分店
- 財務部
- 策劃部
- 長沙子公司
- 財務部
- 策劃部複製程式碼
輸出稍有調整, 以顯示直觀的層級. 大家可以自行修改下程式碼, 來給department加入level屬性, 以便輸出更完美的結構.
到此, 我們算是解決了組織架構的問題了, 以後不管是增加了什麼層級, 我們都只需要使用Department來表示即可, 而無需增加一個類了.
故事之後
照例, 我們來縷縷小光設計的這套系統的”類關係”:
結構相當簡單, 實際上就是一個類, 其中關鍵的是: 這個類中有一個list屬性(subDepartments)包含的是一組該類. 有點遞迴的感覺.
這個就是我們今天想說的 — 組合模式
組合模式
又叫部分整體模式,通過組合的方式, 建立一個包含了一組自己的物件組(List) 的類(Department). 從而達成了用一個類來遞迴地表示一個整體.組合模式通常用來解決樹形結構的表達問題, 例如本例中的組織結構.
所謂組合的方式, 就是建立一個包含了一組自己的物件組的類
注: 我們這裡實現的相對簡單, 旨在說明模式的形式.
實際場景中, Department可能是一個抽象的, 例如有一個抽象方法來執行該部門的職責, 有不同的具體部門實現來實現其職責. 從而有不同的”Department實現”來組合另一個大的Department節點.
擴充套件閱讀一
前面說到, 組合模式通常用來處理樹形結構的表達問題. 而我們的使用者介面實現通常就是一個UI元素的節點樹. 例如HTML中的div中可能有button, 還會有其他div層級, 依次往下; Java SE中的介面實現AWT和Swing包也到處是組合模式的體現; Android中的View體系, View和ViewGroup, Layout層級樹也都是組合模式的體現.
在此我們以Android的View體系為例, 簡單描述下.
眾所周知, Android的介面實際上就是一顆大樹, Hierarchy Viewer中展示的就是一個樹形結構. 我們來看下Android是怎麼利用組合模式實現的.
如下:
- ViewGroup就是相當於例子中的Department, 這裡面比我們的例子複雜的是, ViewGroup是擴充套件自View的, View實際上我們可以理解為是葉子節點, 也就是最小的組成單元了, 不再有以下的層級了.
- ViewGroup中包含了一組View物件, 因為ViewGroup是View的子類, 故而我們也可以說ViewGroup中包含了一組ViewGroup和/或View. 進入形成了我們之前說的遞迴, 生成一個樹形結構.
關於View的體系, 在此不細述了.
搭建好彈性可擴充套件的組織架構體系, 小光又開始將眼光轉移到市場, 轉移到業務了, 準備著新年大展拳腳~