题外话
本来是周更的频率, 因为真实的"小光"真实地逃离了北京, 回了武汉, 回了老家, 处理了一些私人事务. 也就有快一个月时间没有更新了, 抱歉.
年终总结也都没有时间写, 抽空写这么一篇, 提前祝大家新年快乐, 过年回家路上平安顺利.
前情提要
上集讲到, 小光统一了各个分店的制作热干面的流程, 并且引入模板方法在不改变热干面的制作流程基础上可以定制其中某些步骤. 通过这些措施, 小光热干面的品牌性也是更加深入人心, 生意红红火火.
此时的小光, 有了更多的时间去思考公司的前程, 他认为, 一个良好的有序的组织架构是公司发展的必要点. 而此时, 小光也有觉得是时候梳理下公司的组织架构了.
所有示例源码已经上传到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("\n");
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实现"来组合另一个大的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的体系, 在此不细述了.
搭建好弹性可扩展的组织架构体系, 小光又开始将眼光转移到市场, 转移到业务了, 准备着新年大展拳脚~