引例
在介绍之前我们先来看一个例子,是关于公司的人事管理系统的:
这是一个典型的树形结构,那么我们怎么才能用代码表示他呢?
分析:
总共有两种不同性质的节点:
- 有分支的节点:
- 根节点:总经理
- 树枝节点:研发部经理
- 无分支的节点:
- 叶子节点:开发人员
那这么说,定义三个类不就行了,如下图:
那我们就按照这个类图实现一下代码:
首先是 根节点接口 :
/**
* 根节点接口
*
* @author wang suo
* @version 1.0
* @date 2020/12/21 0021 22:36
*/
public interface IRoot {
/**
* 得到总经理的信息
*
* @return 返回信息
*/
String getInfo();
/**
* 总经理下边要有小兵-要能增加小兵-比如研发经理-这是树枝节点
*
* @param iBranch 有分支的节点
*/
void add(IBranch iBranch);
/**
* 增加树叶节点的方法
*
* @param iLeaf 叶子节点
*/
void add(ILeaf iLeaf);
/**
* 还要能进行遍历-不可能总经理不知道他收下有多少人
*
* @return 返回所有手下的[集合]
*/
List getSubordinateInfo();
}
根节点接口的 实现类 ,就是我们这里的总经理:
/**
* 根节点的实现类
*
* @Author wang suo
* @Date 2020/12/22 0022 11:03
* @Version 1.0
*/
public class Root implements IRoot {
/**
* 保存根节点下的所有树枝节点和树叶节点
*/
private List<Object> 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;
}
@Override
public String getInfo() {
String info = "";
info = "名称: " + this.name;
info = info + "\t职位: " + this.position;
info = info + "\t薪水: " + this.salary;
return info;
}
/**
* 增加树叶节点
*
* @param iBranch 有分支的节点
*/
@Override
public void add(IBranch iBranch) {
subordinateList.add(iBranch);
}
/**
* 增加叶子节点-直属于总经理的-比如秘书
*
* @param iLeaf 叶子节点
*/
@Override
public void add(ILeaf iLeaf) {
subordinateList.add(iLeaf);
}
@Override
public List getSubordinateInfo() {
return this.subordinateList;
}
}
有分支的节点 接口:
/**
* 有分支的节点接口
*
* @author wang suo
* @version 1.0
* @date 2020/12/21 0021 22:38
*/
public interface IBranch {
/**
* 得到自己的信息
*
* @return 返回信息
*/
String getInfo();
/**
* 增加数据节点-例如研发部下面的研发一组
*
* @param iBranch 有分支的节点
*/
void add(IBranch iBranch);
/**
* 增加树叶节点的方法
*
* @param iLeaf 叶子节点
*/
void add(ILeaf iLeaf);
/**
* 获得下级信息
*
* @return 返回所有手下的[集合]
*/
List getSubordinateInfo();
}
有分支的节点实现:
/**
* 分支的节点实现
*
* @Author wang suo
* @Date 2020/12/22 0022 11:16
* @Version 1.0
*/
public class Branch implements IBranch {
/**
* 存储子节点的信息
*/
private List<Object> 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;
}
@Override
public String getInfo() {
String info = "";
info = "名称: " + this.name;
info = info + "\t职位: " + this.position;
info = info + "\t薪水: " + this.salary;
return info;
}
@Override
public void add(IBranch iBranch) {
this.subordinateList.add(iBranch);
}
@Override
public void add(ILeaf iLeaf) {
this.subordinateList.add(iLeaf);
}
@Override
public List getSubordinateInfo() {
return this.subordinateList;
}
}
叶子节点 的接口:
/**
* 叶子节点的接口
*
* @author wang suo
* @version 1.0
* @date 2020/12/21 0021 22:38
*/
public interface ILeaf {
/**
* 获取信息
*
* @return 返回信息
*/
String getInfo();
}
叶子节点的实现:
/**
* 叶子节点的实现
*
* @Author wang suo
* @Date 2020/12/22 0022 11:20
* @Version 1.0
*/
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;
}
@Override
public String getInfo() {
String info = "";
info = "名称: " + this.name;
info = info + "\t职位: " + this.position;
info = info + "\t薪水: " + this.salary;
return info;
}
}
所有的根节点,树枝节点和叶子节点都已经实现了,随后我们实现一下 环境类
:
/**
* 场景类
*
* @Author wang suo
* @Date 2020/12/22 0022 11:22
* @Version 1.0
*/
public class Client {
public static void main(String[] args) {
// 首先产生一个根节点
IRoot ceo = new Root("王大麻子", "总经理", 100000);
// 产生 3 个部门经理
Branch developDep = new Branch("刘大瘸子", "研发部门经理", 10000);
Branch salesDep = new Branch("马二拐子", "销售部门经理", 20000);
Branch financeDep = new Branch("赵三驼子", "财务部门经理", 30000);
// 开发部门的3个小组长
IBranch firstDevGroup = new Branch("杨三乜斜", "开发一组组长", 5000);
IBranch secondDevGroup = new Branch("吴大棒槌", "开发二组组长", 6000);
// 剩下的就是我们这些小兵了,就是路人甲,路人乙
ILeaf a = new Leaf("a", "开发人员", 2000);
ILeaf b = new Leaf("b", "开发人员", 2000);
ILeaf c = new Leaf("c", "开发人员", 2000);
ILeaf d = new Leaf("d", "开发人员", 2000);
ILeaf e = new Leaf("e", "开发人员", 2000);
ILeaf f = new Leaf("f", "开发人员", 2000);
ILeaf g = new Leaf("g", "开发人员", 2000);
ILeaf h = new Leaf("h", "销售人员", 5000);
ILeaf i = new Leaf("i", "销售人员", 4000);
ILeaf j = new Leaf("j", "财务人员", 5000);
ILeaf k = new Leaf("k", "CEO秘书", 8000);
ILeaf zhengLaoLiu = new Leaf("郑老六", "研发部副总", 20000);
// 该产生的人都产生出来了,然后我们怎么组装这棵树
// 首先是定义总经理下有三个部门经理
ceo.add(developDep);
ceo.add(salesDep);
ceo.add(financeDep);
// 总经理下还有一个秘书
ceo.add(k);
// 定义研发部门下的结构
developDep.add(firstDevGroup);
developDep.add(secondDevGroup);
// 研发部经理下还有一个副总
developDep.add(zhengLaoLiu);
// 看看开发两个开发小组下有什么
firstDevGroup.add(a);
firstDevGroup.add(b);
firstDevGroup.add(c);
secondDevGroup.add(d);
secondDevGroup.add(e);
secondDevGroup.add(f);
// 再看销售部下的人员情况
salesDep.add(h);
salesDep.add(i);
//最后一个财务
financeDep.add(j);
//树状结构写完毕,然后我们打印出来
System.out.println(ceo.getInfo());
//打印出来整个树形
getAllSubordinateInfo(ceo.getSubordinateInfo());
}
/**
* 遍历并打印整个树
*
* @param subordinateList 树或子树
*/
private static void getAllSubordinateInfo(List subordinateList) {
for (Object o : subordinateList) {
if (o instanceof Leaf) {
System.out.println(((Leaf) o).getInfo());
} else {
IBranch branch = (IBranch) o;
System.out.println(branch.getInfo());
// 递归调用
getAllSubordinateInfo(branch.getSubordinateInfo());
}
}
}
}
执行结果:
名称: 王大麻子 职位: 总经理 薪水: 100000
名称: k 职位: CEO秘书 薪水: 8000
名称: 刘大瘸子 职位: 研发部门经理 薪水: 10000
名称: 郑老六 职位: 研发部副经理 薪水: 20000
名称: 杨三乜斜 职位: 开发一组组长 薪水: 5000
名称: a 职位: 开发人员 薪水: 2000
名称: b 职位: 开发人员 薪水: 2000
名称: c 职位: 开发人员 薪水: 2000
名称: 吴大棒槌 职位: 开发二组组长 薪水: 6000
名称: d 职位: 开发人员 薪水: 2000
名称: e 职位: 开发人员 薪水: 2000
名称: f 职位: 开发人员 薪水: 2000
名称: 马二拐子 职位: 销售部门经理 薪水: 20000
名称: h 职位: 销售人员 薪水: 5000
名称: i 职位: 销售人员 薪水: 4000
名称: 赵三驼子 职位: 财务部经理 薪水: 30000
名称: j 职位: 财务人员 薪水: 5000
存在的问题
现在我们来分析一下上面的代码和架构出现的问题:
- 我们发现
Root
类和Branch
类几乎一摸一样,所以这两个可以合并; - 另外每个节点都有
getInfo
方法,所以我们可以提取出一个抽象类;
修改之后的类图
精简之后的代码显得格外简洁,而且环境类只需要直接和抽象类 Corp
关联就可以了,代码如下:
抽象 公司职员类
:
/**
* 抽象公司职员类
*
* @Author wang suo
* @Date 2020/12/22 0022 19:11
* @Version 1.0
*/
public abstract class AbstractCorp {
/**
* 公司每个人都有名称
*/
private String name = "";
/**
* 公司每个人都有职位
*/
private String position = "";
/**
* 公司每个人都有薪水
*/
private int salary = 0;
/**
* 构造方法
*
* @param name 名称
* @param position 职位
* @param salary 薪水
*/
public AbstractCorp(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
/**
* 获取员工信息
*
* @return 信息
*/
public String getInfo() {
String info = "";
info = "名称: " + this.name;
info = info + "\t职位: " + this.position;
info = info + "\t薪水: " + this.salary;
return info;
}
}
树枝节点:
/**
* 树枝节点
*
* @Author wang suo
* @Date 2020/12/22 0022 20:19
* @Version 1.0
*/
public class Branch extends AbstractCorp {
/**
* 领导下边有哪些下级领导和小兵
*/
private List<AbstractCorp> subordinateList = new ArrayList<>();
/**
* 构造方法
*
* @param name 名称
* @param position 职位
* @param salary 薪水
*/
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
/**
* 增加一个下属
*
* @param corp 树枝
*/
public void addSubordinate(AbstractCorp corp) {
subordinateList.add(corp);
}
/**
* 我有哪些下属
*
* @return 返回下属
*/
public List<AbstractCorp> getSubordinate() {
return subordinateList;
}
}
树叶节点:
/**
* 叶子
*
* @Author wang suo
* @Date 2020/12/22 0022 20:29
* @Version 1.0
*/
public class Leaf extends AbstractCorp {
/**
* 构造方法
*
* @param name 名称
* @param position 职位
* @param salary 薪水
*/
public Leaf(String name, String position, int salary) {
super(name, position, salary);
}
}
修改之后的场景类:
/**
* 修改之后的场景类
*
* @Author wang suo
* @Date 2020/12/22 0022 20:26
* @Version 1.0
*/
public class Client {
public static void main(String[] args) {
//首先是组装一个组织结构出来
Branch ceo = compositeCorpTree();
//首先把CEO的信息打印出来:
System.out.println(ceo.getInfo());
//然后是所有员工信息
System.out.println(getTreeInfo(ceo));
}
//把整个树组装出来
public static Branch compositeCorpTree() {
//首先产生总经理CEO
Branch root = new Branch("王大麻子", "总经理", 100000);
//把三个部门经理产生出来
Branch developDep = new Branch("刘大瘸子", "研发部门经理", 10000);
Branch salesDep = new Branch("马二拐子", "销售部门经理", 20000);
Branch financeDep = new Branch("赵三驼子", "财务部经理", 30000);
//再把三个小组长产生出来
Branch firstDevGroup = new Branch("杨三乜斜", "开发一组组长", 5000);
Branch secondDevGroup = new Branch("吴大棒槌", "开发二组组长", 6000);
//把所有的小兵都产生出来
Leaf a = new Leaf("a", "开发人员", 2000);
Leaf b = new Leaf("b", "开发人员", 2000);
Leaf c = new Leaf("c", "开发人员", 2000);
Leaf d = new Leaf("d", "开发人员", 2000);
Leaf e = new Leaf("e", "开发人员", 2000);
Leaf f = new Leaf("f", "开发人员", 2000);
Leaf g = new Leaf("g", "开发人员", 2000);
Leaf h = new Leaf("h", "销售人员", 5000);
Leaf i = new Leaf("i", "销售人员", 4000);
Leaf j = new Leaf("j", "财务人员", 5000);
Leaf k = new Leaf("k", "CEO秘书", 8000);
Leaf zhengLaoLiu = new Leaf("郑老六", "研发部副经理", 20000);
//开始组装
//CEO下有三个部门经理和一个秘书
root.addSubordinate(k);
root.addSubordinate(developDep);
root.addSubordinate(salesDep);
root.addSubordinate(financeDep);
//研发部经理
developDep.addSubordinate(zhengLaoLiu);
developDep.addSubordinate(firstDevGroup);
developDep.addSubordinate(secondDevGroup);
//看看开发两个开发小组下有什么
firstDevGroup.addSubordinate(a);
firstDevGroup.addSubordinate(b);
firstDevGroup.addSubordinate(c);
secondDevGroup.addSubordinate(d);
secondDevGroup.addSubordinate(e);
secondDevGroup.addSubordinate(f);
//再看销售部下的人员情况
salesDep.addSubordinate(h);
salesDep.addSubordinate(i);
//最后一个财务
financeDep.addSubordinate(j);
return root;
}
public static String getTreeInfo(Branch root) {
List<AbstractCorp> subordinateList = root.getSubordinate();
String info = "";
for (AbstractCorp corp : subordinateList) {
if (corp instanceof Leaf) {
info = info + corp.getInfo() + "\n";
} else {
// 是个小头目
info = info + corp.getInfo() + "\n" + getTreeInfo((Branch) corp);
}
}
return info;
}
}
这就是 组合模式
。
组合模式
组合模式也叫合成模式,有事又叫做部分-整体模式,主要是用来描述整体和部分之间的关系。
组合模式的通用类图:
抽象组件:
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);
}
}
}
}
我们发现在环境类中直接使用
Composite root = new Composite();
这个了,违反了依赖倒置原则。
我们再来分析一下组合模式的优点和缺点:
- 优点:
- 高层模块调用简单:局部和整体对调用者来说没有区别;
- 节点自由增加:非常容易扩展,符合开闭原则;
- 缺点:
- 违反了依赖倒置原则;
组合模式的两种实现
其实组合模式是有两种实现的,分别是 透明模式 和 安全模式 ,我们上面用到的是安全模式,那么透明模式是怎么样的?
类图如下:
我们与安全模式对比一下就非常清楚了,透明模式是把所有的方法都放在抽象类中,这样的话对于树叶节点来说是不安全的,因为在运行期间会遇到问题。