什么是组合模式?
将对象组合成树形结构以表示“部分-整体”的层次结构,用户对单个对象和组合对象的使用具有一致性。
实现
// 抽象公司
type Company interface {
Add(company Company)
Remove(company Company)
Display(depth int)
LineOfDuty()
}
// 具体公司
type ConcreteCompany struct {
name string
companySlice []Company
}
func NewConcreteCompany(name string) *ConcreteCompany {
return &ConcreteCompany{name: name}
}
func (this *ConcreteCompany) Add(company Company) {
this.companySlice = append(this.companySlice, company)
}
func (this *ConcreteCompany) Remove(company Company) {
for idx, comp := range this.companySlice {
if comp == company {
this.companySlice = append(this.companySlice[:idx], this.companySlice[idx+1:]...)
}
}
}
func (this *ConcreteCompany) Display(depth int) {
for i := 0; i < depth; i++ {
fmt.Print("-")
}
fmt.Println(this.name)
for _, company := range this.companySlice {
company.Display(depth + 1)
}
}
func (this *ConcreteCompany) LineOfDuty() {
for _, company := range this.companySlice {
company.LineOfDuty()
}
}
// 人力资源部
type HRDepartment struct {
name string
companySlice []Company
}
func NewHRDepartment(name string) *HRDepartment {
return &HRDepartment{name: name}
}
func (this *HRDepartment) Add(company Company) {
}
func (this *HRDepartment) Remove(company Company) {
}
func (this *HRDepartment) Display(depth int) {
for i := 0; i < depth; i++ {
fmt.Print("-")
}
fmt.Println(this.name)
}
func (this *HRDepartment) LineOfDuty() {
fmt.Println("员工招聘培训管理: ", this.name)
}
// 财务部
type FinanceDepartment struct {
name string
companySlice []Company
}
func NewFinanceDepartment(name string) *FinanceDepartment {
return &FinanceDepartment{name: name}
}
func (this *FinanceDepartment) Add(company Company) {
}
func (this *FinanceDepartment) Remove(company Company) {
}
func (this *FinanceDepartment) Display(depth int) {
for i := 0; i < depth; i++ {
fmt.Print("-")
}
fmt.Println(this.name)
}
func (this *FinanceDepartment) LineOfDuty() {
fmt.Println("公司财务收支管理: ", this.name)
}
func TestNewConcreteCompany(t *testing.T) {
// 总公司
root := NewConcreteCompany("东莞总公司")
root.Add(NewFinanceDepartment("总公司财务部"))
root.Add(NewHRDepartment("总公司人力资源部"))
// 上海分公司
companySH := NewConcreteCompany("上海分公司")
companySH.Add(NewFinanceDepartment("上海公司财务部"))
companySH.Add(NewHRDepartment("上海公司人力资源部"))
root.Add(companySH)
// 南京分公司
companyNJ := NewConcreteCompany("南京分公司")
companyNJ.Add(NewFinanceDepartment("南京公司财务部"))
companyNJ.Add(NewHRDepartment("南京公司人力资源部"))
root.Add(companyNJ)
// 深圳分公司
companySZ := NewConcreteCompany("深圳分公司")
companySZ.Add(NewFinanceDepartment("深圳公司财务部"))
companySZ.Add(NewHRDepartment("深圳公司人力资源部"))
root.Add(companySZ)
// 取消上海分公司
root.Remove(companySH)
fmt.Println("结构图")
root.Display(1)
fmt.Println("职责")
root.LineOfDuty()
}
// === RUN TestNewConcreteCompany
// 结构图
// -东莞总公司
// --总公司财务部
// --总公司人力资源部
// --南京分公司
// ---南京公司财务部
// ---南京公司人力资源部
// --深圳分公司
// ---深圳公司财务部
// ---深圳公司人力资源部
// 职责
// 公司财务收支管理: 总公司财务部
// 员工招聘培训管理: 总公司人力资源部
// 公司财务收支管理: 南京公司财务部
// 员工招聘培训管理: 南京公司人力资源部
// 公司财务收支管理: 深圳公司财务部
// 员工招聘培训管理: 深圳公司人力资源部
// /--- PASS: TestNewConcreteCompany (0.00s)
// PASS
优点
- 高层模块调用简单。一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,高层模块不必关心自己处理的是单个对象还是整个组合结构;
- 节点自由增加;
缺点
- 使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒转原则。
使用场景
- 维护和展示部分-整体关系的场景(如树形菜单、文件和文件夹管理);
- 从一个整体中能够独立出部分模块或功能的场景。