“面向对象编程”是对现实世界的抽象,是通过代码构建出一个虚拟的世界。
如下图所示,这是一个歌唱比赛节目的游戏规则示例(为便于举例,采用了大家耳熟能详的歌手名称,不代表歌手的演艺实力),如何在虚拟世界中通过代码实现这样的游戏规则呢?
上图表述了游戏的三个阶段:
1. 16进8淘汰赛:由16名参赛者一一对决,票数优先者晋级下一轮比赛,票数落后者被淘汰。
2. 8进4晋级赛:由8名参赛者一一对决,票数优先者晋级到本赛区决赛。
3. 赛区争夺战:由前两个阶段产生出本赛区冠亚军共2名,组成赛区战队,与另一赛区战队对决。对决票数优先战队将获得总决赛的额外加分权。
为了简化示例,后续比赛规则在这里不展开描述了。值得一提的是:每一个参赛单元可以是独立的一个人,也可以是由多个人形成的组合,如上图所示的F4和动力火车,则是分别由4人和2人形成的组合。并且在比赛的第3阶段,是由2个参赛单元组成的战队。
那么问题来了,参与比赛的“单元”会出现多种不同的情况:
1. 独立的一个人
2. 多个人形成的组合
3. 2个参赛单元组成的战队
3A. 独立的一个人和独立的一个人,组成战队
3B. 独立的一个人和多个人形成的组合,组成战队
3C. 组合和组合,组成战队
针对这种情况,可以采用组合模式来对代码构造的虚拟世界进行建模。
首先建立最小参赛单元:Unit
// 参赛单元 public class Unit { // 参赛单元 名称 public string Name { get; set; } // 获得票选数量 public int NumberOfVotes { get; set; } // 子级单元 // 若为多人形成的组合,则此属性为形成组合的各个人 public List<Unit> SubUnits { get; set; } }
再建立两个参赛单元的对决函数:Battle
// 两个参赛单元对决,得到获胜的一方 public Unit Battle(Unit unitA, Unit unitB) { if (unitA.NumberOfVotes > unitB.NumberOfVotes) // 若A参赛单元的票选数量多于B return unitA; // 则判定A胜利 return unitB; // 否则判定B胜利 // 总票数为奇数计票可采用此方法,总票数为偶数计票可能出现两个参赛单元票数相同的情况,故不适用于此方法 }
最后建立模型,调用对决函数Battle,得到对决结果
var guo = new Unit { Name = "郭富城", NumberOfVotes = 800 }; // 这是郭富城,800票 var f4 = new Unit { Name = "F4", // 这是F4 NumberOfVotes = 799, // F4有799票 SubUnits = new List<Unit> // F4组合由以下这些成员组成 { new Unit{Name = "言承旭"}, new Unit{Name = "吴建豪"}, new Unit{Name = "周渝民"}, new Unit{Name = "朱孝天"}, } }; var winnerOfElimination = Battle(guo, f4); // 郭富城800多余F4的799,郭富城获胜 var red = new Unit { Name = "红方战队", // 这是红方战队 NumberOfVotes = 998, // 红方战队有998票 SubUnits = new List<Unit> // 红方战队由以下这些成员组成 { guo, new Unit{ Name="李宗盛"} } }; var blue = new Unit { Name = "蓝方战队", // 这是蓝方战队 NumberOfVotes = 999, // 蓝方战队有999票 SubUnits = new List<Unit> // 蓝方战队由以下这些成员组成 { new Unit{Name = "张学友"}, new Unit{Name = "张信哲"}, } }; var winnerOfDivision = Battle(red, blue); // 红方战队998少于蓝方战队999票,蓝方获胜
若战队由独立一个人和组合形成,那么这个战队的模型即为:
var team = new Unit { Name = "独立一个人和组合形成的战队", NumberOfVotes = 888, SubUnits = new List<Unit> { new Unit { Name = "F4", SubUnits = new List<Unit> { new Unit{Name = "言承旭"}, new Unit{Name = "吴建豪"}, new Unit{Name = "周渝民"}, new Unit{Name = "朱孝天"}, } }, new Unit { Name = "李宗盛" } } };
至此,我们已经可以通过组合模式,在虚拟世界中构建一个简单的歌唱比赛节目的游戏规则。
由以上组合模式中的数据模型,我们不难看出它独有的特性,即:对象下面的子级属性,和对象本身是同一类型。
所以组合模式中的对象,其下级对象、或下级对象集合,都是可以向再下级无限延伸的。并且单个对象和多个对象形成的组合,在业务层面上被同等对待,他们具有一致性。它常被我们用在树形菜单、多级分类、企业组织架构等功能实现上。
好了,组合模式介绍到这里,留下一个问题供读者思考:
组合模式常被使用在企业组织架构的功能实现上,那么一个如下图所示的组织架构,我们如何遍历得到“技术部”这一个架构节点?你的遍历算法如何让“时间复杂度”达到最优?