一个公司是由每一个工作的成员组成,每一个成员有不同的属性(名字,职位,薪水),根据不同的等级,构成一个树形的结构。总经理是这个棵树的根节点,因为他没有上级,部门经理是树枝节点,因为他既有上级也有下级,小喽啰是叶节点,因为他是最底层的苦逼,没有下级。现用组合模式将这个树展示出来,类图:
代码如下:
name = $name; $this->position = $position; $this->salary = $salary; } public function getInfo() { $return = "姓名:".$this->name."\t"; $return .= "职位:".$this->position."\t"; $return .= "薪水:".$this->salary."\n"; return $return; } } class Leaf extends Corp{ } class Branch extends Corp{ private $subordinateList = array(); public function addSubordinate(Corp $corp){ array_push($this->subordinateList, $corp); } public function getSubordinateList(){ return $this->subordinateList; } } $root = new Branch('马总','总经理',100000); $branch1 = new Branch('罗总','研发部门经理',20000); $branch2 = new Branch('高总','销售部门经理',80000); $leaf1 = new Leaf('张三','开发人员',7000); $leaf2 = new Leaf('李四','开发人员',8000); $leaf3 = new Leaf('二蛋','销售人员',10000); $leaf4 = new Leaf('狗子','销售人员',15000); $root->addSubordinate($branch1); $branch1->addSubordinate($leaf1); $branch1->addSubordinate($leaf2); $root->addSubordinate($branch2); $branch2->addSubordinate($leaf3); $branch2->addSubordinate($leaf4); function getTreeInfo($branch){ echo $branch->getInfo(); $subordinateList = $branch->getSubordinateList(); foreach ($subordinateList as $value) { if($value instanceof Branch){ getTreeInfo($value); }else{ echo $value->getInfo(); } } } getTreeInfo($root); ?>
运行结果:
姓名:马总 职位:总经理 薪水:100000
姓名:罗总 职位:研发部门经理 薪水:20000
姓名:张三 职位:开发人员 薪水:7000
姓名:李四 职位:开发人员 薪水:8000
姓名:高总 职位:销售部门经理 薪水:80000
姓名:二蛋 职位:销售人员 薪水:10000
姓名:狗子 职位:销售人员 薪水:15000
[Finished in 0.1s]
组合模式的定义
组合模式也叫合成模式,也叫部分-整体模式,主要是用来描述部分与整体的关系。其定义为:将对象组合成树形结构以表示”部分-整体“的层次结构,使得用户对单个对象和组合对象的使用具有一致性。组合模式主要由三个角色构成
1、Component抽象构件角色
定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性,比如例子中的Corp类。
2、Leaf叶子构件
3、Composite树枝构件
组合树枝节点和叶子节点形成一个树形结构
组合模式的优点
1、高层模块调用简单
一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
2、节点自己增加
只要找到它的父节点就成,非常容易扩展,符合开闭原则。
组合模式的缺点
组合模式有一个非常明显的缺点,看到在场景类中的定义,提到树叶和树枝使用时的定义了吗?直接使用了实现类!这在面向接口编程上是很不恰当的,与依赖代表团原则冲突,限制了接口中的影响范围。
组合模式的使用场景
1、维护和展示部分-整体关系的场景,如树形菜单、文件、文件夹管理。
2、从一个整体中的能够独立出总价模块或功能的场景。
组合模式的注意事项
只要是树形结构,就要考虑使用组合模式,这个一定要记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,就应该考虑一下组合模式。
组合模式的扩展
1、真实的组合模式(略过)
2、透明的组合模式
例子中是安全的组合模式,透明模式是把用来组合的方法放到抽象类中(略过)
3、组合模式的遍历
例子中实现了树的从上级向下级遍历,如果现随便选中一个叶节点,如何向上级做遍历?
其实也很简单,只要在添加节点时设置其父节点既可,代码如下:
private $parent = null; public function __construct( $name, $position, $salary ) { $this->name = $name; $this->position = $position; $this->salary = $salary; } public function getInfo() { $return = "姓名:".$this->name."\t"; $return .= "职位:".$this->position."\t"; $return .= "薪水:".$this->salary."\n"; return $return; } public function setParent($parent){ $this->parent = $parent; } public function getParent(){ return $this->parent; } } class Leaf extends Corp{ } class Branch extends Corp{ private $subordinateList = array(); public function addSubordinate(Corp $corp){ $corp->setParent($this); array_push($this->subordinateList, $corp); } public function getSubordinateList(){ return $this->subordinateList; } } $root = new Branch('马总','总经理',100000); $branch1 = new Branch('罗总','研发部门经理',20000); $branch2 = new Branch('高总','销售部门经理',80000); $leaf1 = new Leaf('张三','开发人员',7000); $leaf2 = new Leaf('李四','开发人员',8000); $leaf3 = new Leaf('二蛋','销售人员',10000); $leaf4 = new Leaf('狗子','销售人员',15000); $root->addSubordinate($branch1); $branch1->addSubordinate($leaf1); $branch1->addSubordinate($leaf2); $root->addSubordinate($branch2); $branch2->addSubordinate($leaf3); $branch2->addSubordinate($leaf4); function getParentInfo($leaf){ echo $leaf->getInfo(); $parent = $leaf->getParent(); if($parent instanceof branch) getParentInfo($parent); } getParentInfo($leaf4); ?>
运行结果:
姓名:狗子 职位:销售人员 薪水:15000
姓名:高总 职位:销售部门经理 薪水:80000
姓名:马总 职位:总经理 薪水:100000
[Finished in 0.2s]
代码中黑体部分为与上一例的区别。