小 Y 科普: 家谱又称族谱宗谱等它以记载父系家族世系人物为中心, 由正史中的帝王本纪及王侯列传年表等演变而来是一种特殊的文献, 就其内容而言, 是中国五千年文明史中具有平民特色的文献, 记载的是同宗共祖血缘集团世系人物和事迹等方面情况的历史图籍
Now,how to 实现 小 J's 族谱
一小 J 族谱简略版
从最顶层的第一代 J 开始, 一代代往下记录下去, 这很明显就是一个树状结构, 现在小 Y 要做的就是通过最合适的方式把小 J 的族谱图遍历出来
二树状图的拆解利器 - 组合模式
1. 组合模式的定义
组合模式也叫合成模式, 有时又叫做部分 - 整体模式, 主要是用来描述部分与整体的关系, 将对象组合成树形结构以表示部分 - 整体的层次结构, 使得用户对单个对象和组合对象的使用具有一致性
2. 组合模式的角色介绍 (组合模式有两种实现: 安全模式和透明模式)
Component 抽象构件角色
定义参加组合对象的共有方法和属性, 可以定义一些默认的行为或属性
Leaf 叶子构件
Leaf 叶子构件叶子对象, 其下再也没有其他的分支, 也就是遍历的最小单位
Composite 树枝构件
树枝对象, 它的作用是组合树枝节点和叶子节点形成一个树形结构组合模式的重点就在树枝构件
3. 组合模式的使用场景
只要是树形结构或者只要是要体现局部和整体的关系的时候, 而且这种关系还可能比较深, 就要考虑一下组合模式
从一个整体中能够独立出部分模块或功能的场景
维护和展示部分 - 整体关系的场景
4. 安全模式和透明模式的具体实现
(1) 安全模式
抽象构件
- public abstract class Component {
- // 个体和整体都具有
- public void operation(){
- // 编写业务逻辑
- }
- }
树枝构件
- public class Composite extends Component {
- // 构件容器
- private List<Component> componentArrayList = new ArrayList<Component>();
- // 增加一个叶子构件或树枝构件
- public void add(Component component){
- this.componentArrayList.add(component);
- }
- // 删除一个叶子构件或树枝构件
- public void remove(Component component){
- this.componentArrayList.remove(component);
- }
- // 获得分支下的所有叶子构件和树枝构件
- public List<Component> getChildren(){
- return this.componentArrayList;
- }
- }
树叶构件
- public class Leaf extends Component {
- /*
- * 可以覆写父类方法
- * public void operation(){
- *
- * }
- */
- }
- Client
- public class Client {
- public static void main(String[] args) {
- // 创建一个根节点
- Composite root = new Composite();
- root.operation();
- // 创建一个树枝构件
- Composite branch = new Composite();
- // 创建一个叶子节点
- Leaf leaf = new Leaf();
- // 建立整体
- root.add(branch);
- branch.add(leaf);
- }
- // 通过递归遍历树
- public static void showTree(Composite root){
- for(Component c:root.getChildren()){
- if(c instanceof Leaf){ // 叶子节点
- c.operation();
- }else{ // 树枝节点
- showTree((Composite)c);
- }
- }
- }
- }
(2) 透明模式
抽象构件
- public abstract class Component {
- // 个体和整体都具有
- public void operation(){
- // 编写业务逻辑
- }
- // 增加一个叶子构件或树枝构件
- public abstract void add(Component component);
- // 删除一个叶子构件或树枝构件
- public abstract void remove(Component component);
- // 获得分支下的所有叶子构件和树枝构件
- public abstract List<Component> getChildren();
- }
树枝构件
- 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 List<Component> getChildren(){
- return this.componentArrayList;
- }
- }
树叶构件
- public class Leaf extends Component {
- public void add(Component component){
- // 空实现
- }
- public void remove(Component component){
- // 空实现
- }
- public List<Component> getChildren(){
- // 空实现
- }
- }
- Client
- public class Client {
- public static void main(String[] args) {
- // 创建一个根节点
- Composite root = new Composite();
- root.operation();
- // 创建一个树枝构件
- Composite branch = new Composite();
- // 创建一个叶子节点
- Leaf leaf = new Leaf();
- // 建立整体
- root.add(branch);
- branch.add(leaf);
- }
- // 通过递归遍历树
- public static void showTree(Component root){
- for(Component c:root.getChildren()){
- if(c instanceof Leaf){ // 叶子节点
- c.operation();
- }else{ // 树枝节点
- showTree(c);
- }
- }
- }
- }
4. 安全模式和透明模式的区别
安全模式在抽象组件中只定义一些默认的行为或属性, 它是把树枝节点和树叶节点彻底分开; 透明模式是把用来组合使用的方法放到抽象类中, 不管叶子对象还是树枝对象都有相同的结构, 通过判断确认是叶子节点还是树枝节点, 如果处理不当, 这个会在运行期出现问题, 不是很建议的方式
安全模式与依赖倒置原则冲突; 透明模式的好处就是它基本遵循了依赖倒转原则, 方便系统进行扩展
安全模式在遍历树形结构的的时候需要进行强制类型转换; 在透明模式下, 遍历整个树形结构是比较容易的, 不用进行强制类型转换
三通过安全模式实现遍历小 J 的族谱
抽象构件抽象族员类
- public abstract class PersonMode {
- // 人名
- private String name;
- // 性别
- private String sex;
- // 年龄
- private int age;
- public PersonMode(String name, String sex, int age) {
- this.name = name;
- this.sex = sex;
- this.age = age;
- }
- // 个人信息
- public String getPersonInfo(){
- String info="姓名:"+name+"\t 性别:"+sex+"\t 年龄:"+age;
- return info;
- }
- }
树叶构件
- public class PersonLeaf extends PersonMode {
- // 写一个构造函数
- public PersonLeaf(String name, String sex, int age) {
- super(name, sex, age);
- }
- }
树枝构件
- public class PersonBranch extends PersonMode {
- private List<PersonMode> personModeList=new ArrayList<>();
- public PersonBranch(String name, String sex, int age) {
- super(name, sex, age);
- }
- public void addPerson(PersonMode person){
- this.personModeList.add(person);
- }
- public List<PersonMode> getPersonModeList(){
- return this.personModeList;
- }
- }
- Client
- public class Client {
- public static void main(String[] args) {
- /**
- * 组装小 J 的族谱
- */
- PersonBranch personBranch=getPersonInfo();
- showTree(personBranch);
- }
- private static PersonBranch getPersonInfo(){
- // 第一代 J
- PersonBranch OneJ=new PersonBranch("第一代 J","男",150);
- // 第一代 J 的三个儿子
- PersonBranch JA=new PersonBranch("JA","男",70);
- PersonBranch JB=new PersonBranch("JB","男",60);
- PersonBranch JC=new PersonBranch("JC","男",50);
- //JA 的三个儿子
- PersonBranch JA1=new PersonBranch("JA1","男",40);
- PersonBranch JA2=new PersonBranch("JA2","男",30);
- PersonBranch JA3=new PersonBranch("JA3","男",45);
- //JB 的两个儿子
- PersonBranch JB1=new PersonBranch("JB1","男",40);
- PersonBranch JB2=new PersonBranch("JB2","男",30);
- //JC 的儿子小 J
- PersonBranch xiao_J=new PersonBranch("xiao_J","男",20);
- //JA1 三个儿子
- PersonBranch JA1_1=new PersonBranch("JA1_1","男",18);
- PersonBranch JA1_2=new PersonBranch("JA1_2","男",16);
- PersonBranch JA1_3=new PersonBranch("JA1_3","男",20);
- //JA3 三个儿子
- PersonBranch JA3_1=new PersonBranch("JA3_1","男",16);
- PersonBranch JA3_2=new PersonBranch("JA3_2","男",20);
- PersonBranch JA3_3=new PersonBranch("JA3_3","男",18);
- // 开始组装树状族谱
- // 组装第一代 J 下的三个儿子
- OneJ.addPerson(JA);
- OneJ.addPerson(JB);
- OneJ.addPerson(JC);
- // 组装 JA 的三个儿子
- JA.addPerson(JA1);
- JA.addPerson(JA2);
- JA.addPerson(JA3);
- // 组装 JB 的两个儿子
- JB.addPerson(JB1);
- JB.addPerson(JB2);
- // 组装 JC 的儿子
- JC.addPerson(xiao_J);
- // 组装 JA1 的三个儿子
- JA1.addPerson(JA1_1);
- JA1.addPerson(JA1_2);
- JA1.addPerson(JA1_3);
- // 组装 JA3 的三个儿子
- JA3.addPerson(JA3_1);
- JA3.addPerson(JA3_2);
- JA3.addPerson(JA3_3);
- return OneJ;
- }
- // 通过递归遍历树
- private static void showTree(PersonBranch root){
- System.out.println(root.getPersonInfo());
- for(PersonMode c:root.getPersonModeList()){
- if(c instanceof PersonLeaf){ // 叶子节点
- System.out.println(c.getPersonInfo());
- }else{ // 树枝节点
- showTree((PersonBranch) c);
- }
- }
- }
- }
场景类负责树状结构的建立, 并可以通过递归方式遍历整个树
输出的结果为:
姓名: 第一代 J 性别: 男 年龄: 150
姓名: JA 性别: 男 年龄: 70
姓名: JA1 性别: 男 年龄: 40
姓名: JA1_1 性别: 男 年龄: 18
姓名: JA1_2 性别: 男 年龄: 16
姓名: JA1_3 性别: 男 年龄: 20
姓名: JA2 性别: 男 年龄: 30
姓名: JA3 性别: 男 年龄: 45
姓名: JA3_1 性别: 男 年龄: 16
姓名: JA3_2 性别: 男 年龄: 20
姓名: JA3_3 性别: 男 年龄: 18
姓名: JB 性别: 男 年龄: 60
姓名: JB1 性别: 男 年龄: 40
姓名: JB2 性别: 男 年龄: 30
姓名: JC 性别: 男 年龄: 50
姓名: xiao_J 性别: 男 年龄: 20
四组合模式优缺点
1. 优点
高层模块调用简单局部和整体对调用者来说没有任何区别, 也就是说, 高层模块不必关心自己处理的是单个对象还是整个组合结构, 简化了高层模块的代码
节点自由增加使用了组合模式后, 我们可以看看, 如果想增加一个树枝节点树叶节点十分简单, 只要找到它的父节点就成, 非常容易扩展, 符合开闭原则, 对以后的维护非常有利
2. 缺点
组合模式有一个非常明显的缺点, 在上面的场景类可以看到树枝树叶直接使用了实现类, 这在面向接口编程上是很不恰当的, 与依赖倒置原则冲突, 它限制了你接口的影响范围
来源: https://juejin.im/post/5aa0958f518825555e5d645c