1:主题拆解
①基本介绍
②项目提成分配场景模拟
③安全组合模式升级
④组合模式的优缺点
⑤适用场景
⑥不想做CEO的实习生不是好开发
2:基本介绍
组合模式,将对象组合成树形结构以表示“整体-部分”的层次结构,一种对象结构型模式。
由于在软件开发中存在大量的树形结构,因此组合模式是一种使用频率较高的结构型设计模式,在XML解析、组织结构树处理、文件系统设计等领域,组合模式都得到了广泛应用。
组合模式的分类:
①透明组合模式
透明组合模式中,抽象构件角色中声明了所有对于管理成员对象的方法,透明组合模式是组合模式的标准形式。
②安全组合模式
安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在容器构件类中声明并实现这些方法。
3:项目提成分配场景模拟
1:项目背景
公司承包了一个100万的项目,今天是项目完客户支付尾款的第一天,作为参与这个大项目的实习生“阿发”,有幸的成为了项目提成的成员,突然感觉莫名的激动。
2:提成分配表
3:透明组合模式
①定义部门/个人抽象基类
public abstract class AbstractDomain
{
public string Name { get; set; }
public double Percent { get; set; }
public abstract void Commission(double total);
public abstract void AddChild(Domain domainChild);
}
②具体部门实现
public class Domain : AbstractDomain
{
private List<Domain> DomainChildList = new List<Domain>();
public override void AddChild(Domain domainChild)
{
this.DomainChildList.Add(domainChild);
}
public override void Commission(double total)
{
double result = total * this.Percent / 100;
Console.WriteLine("this {0} 提成 {1}", this.Name, result);
foreach (var domainChild in DomainChildList)
{
domainChild.Commission(result);
}
}
}
③构建提成关系
private static Domain BuildTree()
{
Domain domain = new Domain()
{
Name = "待分配",
Percent = 10
};
#region
Domain domain1 = new Domain()
{
Name = "CEO",
Percent = 30
};
Domain domain2 = new Domain()
{
Name = "各部门共有",
Percent = 70
};
Domain domain21 = new Domain()
{
Name = "实施",
Percent = 20
};
Domain domain22 = new Domain()
{
Name = "测试",
Percent = 10
};
Domain domain23 = new Domain()
{
Name = "销售",
Percent = 30
};
Domain domain24 = new Domain()
{
Name = "开发",
Percent = 40
};
Domain domain241 = new Domain()
{
Name = "经理",
Percent = 20
};
Domain domain242 = new Domain()
{
Name = "主管",
Percent = 15
};
Domain domain243 = new Domain()
{
Name = "开发团队",
Percent = 65
};
Domain domain2431 = new Domain()
{
Name = "项目组1",
Percent = 50
};
Domain domain2432 = new Domain()
{
Name = "项目组2",
Percent = 50
};
Domain domain24321 = new Domain()
{
Name = "项目经理",
Percent = 20
};
Domain domain24322 = new Domain()
{
Name = "开发人员",
Percent = 80
};
Domain domain243221 = new Domain()
{
Name = "高级开发人员",
Percent = 40
};
Domain domain243222 = new Domain()
{
Name = "中级开发人员",
Percent = 30
};
Domain domain243223 = new Domain()
{
Name = "初级开发人员",
Percent = 20
};
Domain domain243224 = new Domain()
{
Name = "实习生",
Percent = 10
};
Domain domain2432241 = new Domain()
{
Name = "实习生1",
Percent = 25
};
Domain domain2432242 = new Domain()
{
Name = "实习生2",
Percent = 25
};
Domain domain2432243 = new Domain()
{
Name = "实习生3",
Percent = 25
};
Domain domain2432244 = new Domain()
{
Name = "阿发",
Percent = 25
};
domain243224.AddChild(domain2432241);
domain243224.AddChild(domain2432242);
domain243224.AddChild(domain2432243);
domain243224.AddChild(domain2432244);
domain24322.AddChild(domain243221);
domain24322.AddChild(domain243222);
domain24322.AddChild(domain243223);
domain24322.AddChild(domain243224);
domain2432.AddChild(domain24321);
domain2432.AddChild(domain24322);
domain243.AddChild(domain2431);
domain243.AddChild(domain2432);
domain24.AddChild(domain241);
domain24.AddChild(domain242);
domain24.AddChild(domain243);
domain2.AddChild(domain21);
domain2.AddChild(domain22);
domain2.AddChild(domain23);
domain2.AddChild(domain24);
domain.AddChild(domain1);
domain.AddChild(domain2);
#endregion
return domain;
}
④上端调用
double total = 1000000;
Domain domain = BuildTree();
domain.Commission(total);
分析:此时就已经现实了项目提成的分配。由于抽象基类中声明了所有对于管理成员对象的方法,即为透明组合模式。
4:安全组合模式升级
经过分析可以发现叶子节点其实是没有AddChild的方法,也没有子节点。此时可以对最后一级叶子节点进行修改。
①抽象基类中去掉AddChild方法
public abstract class AbstractDomain
{
public string Name { get; set; }
public double Percent { get; set; }
public abstract void Commission(double total);
}
②非叶子节点,即容器继承基类并且实现
public class Domain : AbstractDomain
{
private List<Domain> DomainChildList = new List<Domain>();
public void AddChild(Domain domainChild)
{
this.DomainChildList.Add(domainChild);
}
public override void Commission(double total)
{
double result = total * this.Percent / 100;
Console.WriteLine("this {0} 提成 {1}", this.Name, result);
foreach (var domainChild in DomainChildList)
{
domainChild.Commission(result);
}
}
}
③叶子节点继承并且实现
public class DomainLeaf : AbstractDomain
{
public override void Commission(double total)
{
double result = total * this.Percent / 100;
Console.WriteLine("this {0} 提成 {1}", this.Name, result);
}
}
④构建提成关系与上端调用不变
分析:此为安全组合模式,为何称为安全组合模式,在抽象构件角色中没有声明任何用于管理成员对象的方法,即没有定义AddChild方法。并且叶节点也没有此方法。
5:组合模式优缺点
1:优点
①层次控制
组合模式可以清楚定义分层次的复杂对象,表示对象的全部或者部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制
②一致使用构件
客户端可以一致地使用容器构件或者叶子构件,也就是能针对构件抽象层一致性编程
③扩展性好
增加新的容器构件或者叶子构件都很方便,符合开闭原则
④有效针对树形结构
组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子构件与容器构件的递归组合,可以形成复杂的树形结构,但控制树形结构却很简单
2:缺点
①难以限制构件类型
增加新构件时难以限制构件类型,比如希望容器构件中只有某一特定类型的叶子构件,例如一个只能包含图片的文件夹,使用组合模式时不能依赖类型系统来施加这些约束,需要再运行时进行类型检查来实现,过程较为复杂。
6:适用场景
①具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致性对待它们。
②处理树形结构,例如电脑文件夹,部门组织架构,学校组织架构。
③系统中能够分离出叶子构件以及容器构件,而且类型不固定,需要增加新的叶子构件或者容器构件。
④使用场景不是很丰富,一般都是通过数据库中的ParentID来实现的树形结构。
7:不想做CEO的实习生不是好开发
理想很美好,现实很骨干,一个100万的项目兴致勃勃参与分提成只分到了182块。