1. 「引例」
- 举例:公司成员结构:总经理->研发部经历->员工。即 根节点->树枝节点->树叶节点。
「根节点接口」
public interface IRoot {
// 得到总经理的信息
public String getInfo();
// 总经理下边要有小兵,那要能增加小兵,比如研发部总经理,这是个树枝节点
public void add(IBranch branch);
// 那要能增加树叶节点
public void add(ILeaf leaf);
// 既然能增加,那还要能够遍历,不可能总经理不知道他手下有哪些人
public ArrayList getSubordinateInfo();
}
「根节点实现」
public class Root implements IRoot {
//保存根节点下的树枝节点和树叶节点,Subordinate的意思是下级
private ArrayList subordinateList = new ArrayList();
// 根节点的名称
private String name = "";
// 根节点的职位
private String position = "";
// 根节点的薪水
private int salary = 0;
// 通过构造函数传递进来总经理的信息
public Root(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//增加树枝节点
public void add(IBranch branch) {
this.subordinateList.add(branch);
}
//增加叶子节点,比如秘书,直接隶属于总经理
public void add(ILeaf leaf) {
this.subordinateList.add(leaf);
}
//得到自己的信息
public String getInfo() {
String info = "";
info = "名称:" + this.name;
info = info + "\t职位:" + this.position;
info = info + "\t薪水: " + this.salary;
return info;
}
//得到下级的信息
public ArrayList getSubordinateInfo() {
return this.subordinateList;
}
}
「其他含分支节点接口」
public interface IBranch {
//获得信息
public String getInfo();
// 增加数据节点,例如研发部下设的研发一组
public void add(IBranch branch);
// 增加叶子节点
public void add(ILeaf leaf);
// 获得下级信息
public ArrayList getSubordinateInfo();
}
「分支实现」
public class Branch implements IBranch {
//存储子节点的信息
private ArrayList subordinateList = new ArrayList();
// 树枝节点的名称
private String name = "";
// 树枝节点的职位
private String position = "";
// 树枝节点的薪水
private int salary = 0;
// 通过构造函数传递树枝节点的参数
public Branch(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//增加一个子树枝节点
public void add(IBranch branch) {
this.subordinateList.add(branch);
}
//增加一个叶子节点
public void add(ILeaf leaf) {
this.subordinateList.add(leaf);
}
//获得自己树枝节点的信息
public String getInfo() {
String info = "";
info = "名称:" + this.name;
info = info + "\t职位:" + this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
//获得下级的信息
public ArrayList getSubordinateInfo() {
return this.subordinateList;
}
}
「叶子接口」
public interface ILeaf {
//获得自己的信息
public String getInfo();
}
「叶子实现」
public class Leaf implements ILeaf {
// 叶子叫什么名字
private String name = "";
// 叶子的职位
private String position = "";
// 叶子的薪水
private int salary = 0;
// 通过构造函数传递信息
public Leaf(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//最小的小兵只能获得自己的信息了
public String getInfo() {
String info = "";
info = "名称:" + this.name;
info = info + "\t职位:" + this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
}
测试太长了,就是测试上述的程序,可自行去git仓库上看。唯一要说的是打印下属的信息,需要递归打印
//遍历所有的树枝节点,打印出信息
private static String getAllSubordinateInfo(ArrayList subordinateList) {
int length = subordinateList.size();
for (int m = 0; m < length; m++) { //定义一个ArrayList长度,不要在for循环中每次计算
Object s = subordinateList.get(m);
if (s instanceof Leaf) { //是个叶子节点,也就是员工
ILeaf employee = (ILeaf) s;
System.out.println(((Leaf) s).getInfo());
} else {
IBranch branch = (IBranch) s;
System.out.println(branch.getInfo());
//再递归调用
getAllSubordinateInfo(branch.getSubordinateInfo());
}
}
return null;
}
现在需要解决问题。
getInfo
每个接口都有,可以抽象出来。- Root类Branch类可以合并,因为根节点本身就是树枝节点的一种。
- 增加一个
ICorp
接口,是全公司人员信息接口类,大家都可以获取信息。 这里的
ILeaf
是空接口,这里的意义何在?系统扩容时就会体现它的作用!「公司人员接口」
public interface ICorp {
// 每个员工都有信息
public String getInfo();
}
「树叶接口」
public interface ILeaf extends ICorp{}
「树叶实现」
public class Leaf implements ILeaf {
//小兵也有名称
private String name = "";
//小兵也有职位
private String position = "";
//小兵也有薪水,否则谁给你干
private int salary = 0;
//通过一个构造函数传递小兵的信息
public Leaf(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//获得小兵的信息
public String getInfo() {
String info = "";
info = "姓名:" + this.name;
info = info + "\t职位:" + this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
}
「树枝接口」
public interface IBranch extends ICorp{
//能够增加小兵(树叶节点)或者是经理(树枝节点)
public void addSubordinate(ICorp corp);
//能够获得下属的信息
public ArrayList<ICorp> getSubordinate();
}
「树枝实现」
public class Branch implements IBranch {
//领导也是人,也有名字
private String name = "";
//领导和领导不同,也是职位区别
private String position = "";
//领导也是拿薪水的
private int salary = 0;
//领导下边有那些下级领导和小兵
ArrayList<ICorp> subordinateList = new ArrayList<ICorp>();
//通过构造函数传递领导的信息
public Branch(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//增加一个下属,可能是小头目,也可能是个小兵
public void addSubordinate(ICorp corp) {
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList<ICorp> getSubordinate() {
return this.subordinateList;
}
//领导也是人,他也有信息
public String getInfo() {
String info = "";
info = "姓名:" + this.name;
info = info + "\t职位:" + this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
}
再进一步优化,
Leaf
和Branch
都有getInfo
方法,抽象出来
接口没了,改成抽象类。IBranch接口没了。方法都在实现类中,场景类只认定抽象类Corp。
「抽象公司职员类」
public abstract class Corp {
//公司每个人都有名称
private String name = "";
//公司每个人都职位
private String position = "";
//公司每个人都有薪水
private int salary = 0;
/**
* 通过接口的方式传递,我们改变一下习惯,传递进来的参数名以下划线开始
* 这个在一些开源项目中非常常见,一般构造函数都是定义的
*/
public Corp(String _name, String _position, int _salary) {
this.name = _name;
this.position = _position;
this.salary = _salary;
}
//获得员工信息
public String getInfo() {
String info = "";
info = "姓名:" + this.name;
info = info + "\t职位:" + this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
}
「树叶节点」
public class Leaf extends Corp {
//就写一个构造函数,这个是必须的
public Leaf(String _name, String _position, int _salary) {
super(_name, _position, _salary);
}
}
「树枝节点」
public class Branch extends Corp {
//领导下边有那些下级领导和小兵
ArrayList<Corp> subordinateList = new ArrayList<Corp>();
//构造函数是必须的
public Branch(String _name, String _position, int _salary) {
super(_name, _position, _salary);
}
//增加一个下属,可能是小头目,也可能是个小兵
public void addSubordinate(Corp corp) {
this.subordinateList.add(corp);
}
//有哪些下属
public ArrayList<Corp> getSubordinate() {
return this.subordinateList;
}
}
2. 「定义」
Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
- 将对象组合成树形结构以表 示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性
通用类图
-
- Component抽象构件角色
- 定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性,比如例子 中的getInfo就封装到了抽象类中。
- Leaf叶子构件
- 叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
- Composite树枝构件
- 树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。
public abstract class Component {
//个体和整体都具有的共享
public void doSomething() {
//编写业务逻辑
}
}
public class Composite extends Component {
//构件容器
private ArrayList<Component> componentArrayList = new ArrayList<Component>();
//增加一个叶子构件或树枝构件
public void add(Component component) {
this.componentArrayList.add(component);
}
//删除一个叶子构件或树枝构件
public void remove(Component component) {
this.componentArrayList.remove(component);
}
//获得分支下的所有叶子构件和树枝构件
public ArrayList<Component> getChildren() {
return this.componentArrayList;
}
}
public class Leaf extends Component{
/* 可以覆写父类方法 public void doSomething() */
}
public class Client {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite();
root.doSomething();
//创建一个树枝构件
Composite branch = new Composite();
//创建一个叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
branch.add(leaf);
}
//通过递归遍历树
public static void display(Composite root) {
for (Component c : root.getChildren()) {
if (c instanceof Leaf) { //叶子节点
c.doSomething();
} else { //树枝节点
display((Composite) c);
}
}
}
}
3. 「应用」
1. 「优点」
- 高层模块调用简单
- 一棵树形机构中的所有节点都是
Component
,局部和整体对调用者来说没有任何区别 - 也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化高层模块的代码。
- 一棵树形机构中的所有节点都是
节点自由增加
维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
- 从一个整体中能够独立出部分模块或功能的场景。
4. 「注意事项」
📌只要是树形结构,就要考虑使用组合模式,这个一定要记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧。