浅谈组合模式的应用

浅谈组合模式的应用

“面向对象编程”是对现实世界的抽象,是通过代码构建出一个虚拟的世界。

如下图所示,这是一个歌唱比赛节目的游戏规则示例(为便于举例,采用了大家耳熟能详的歌手名称,不代表歌手的演艺实力),如何在虚拟世界中通过代码实现这样的游戏规则呢?

上图表述了游戏的三个阶段:

  1. 16进8淘汰赛:由16名参赛者一一对决,票数优先者晋级下一轮比赛,票数落后者被淘汰。
  2. 8进4晋级赛:由8名参赛者一一对决,票数优先者晋级到本赛区决赛。
  3. 赛区争夺战:由前两个阶段产生出本赛区冠亚军共2名,组成赛区战队,与另一赛区战队对决。对决票数优先战队将获得总决赛的额外加分权。

为了简化示例,后续比赛规则在这里不展开描述了。值得一提的是:每一个参赛单元可以是独立的一个人,也可以是由多个人形成的组合,如上图所示的F4和动力火车,则是分别由4人和2人形成的组合。并且在比赛的第3阶段,是由2个参赛单元组成的战队。

那么问题来了,参与比赛的“单元”会出现多种不同的情况:

  1. 独立的一个人
  2. 多个人形成的组合
  3. 2个参赛单元组成的战队
    • 3A. 独立的一个人和独立的一个人,组成战队
    • 3B. 独立的一个人和多个人形成的组合,组成战队
    • 3C. 组合和组合,组成战队

针对这种情况,可以采用组合模式来对代码构造的虚拟世界进行建模。

首先建立最小参赛单元:Unit

csharp
// 参赛单元public class Unit{ // 参赛单元 名称 public string Name { get; set; } // 获得票选数量 public int NumberOfVotes { get; set; } // 子级单元 // 若为多人形成的组合,则此属性为形成组合的各个人 public List<Unit> SubUnits { get; set; }}

再建立两个参赛单元的对决函数:Battle

csharp
// 两个参赛单元对决,得到获胜的一方public Unit Battle(Unit unitA, Unit unitB){    if (unitA.NumberOfVotes > unitB.NumberOfVotes)    // 若A参赛单元的票选数量多于B        return unitA;                                 // 则判定A胜利    return unitB;                                     // 否则判定B胜利    // 总票数为奇数计票可采用此方法,总票数为偶数计票可能出现两个参赛单元票数相同的情况,故不适用于此方法}

最后建立模型,调用对决函数Battle,得到对决结果

csharp
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票,蓝方获胜

若战队由独立一个人和组合形成,那么这个战队的模型即为:

csharp
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 = "李宗盛"        }    }};

至此,我们已经可以通过组合模式,在虚拟世界中构建一个简单的歌唱比赛节目的游戏规则。

由以上组合模式中的数据模型,我们不难看出它独有的特性,即:对象下面的子级属性,和对象本身是同一类型。

所以组合模式中的对象,其下级对象、或下级对象集合,都是可以向再下级无限延伸的。并且单个对象和多个对象形成的组合,在业务层面上被同等对待,他们具有一致性。它常被我们用在树形菜单、多级分类、企业组织架构等功能实现上。

好了,组合模式介绍到这里,留下一个问题供读者思考:

组合模式常被使用在企业组织架构的功能实现上,那么一个如下图所示的组织架构,我们如何遍历得到“技术部”这一个架构节点?你的遍历算法如何让“时间复杂度”达到最优?

← 返回 Blog