最近看网络新闻,有许多讲儿女不养老的问题,多数都是由于老人有儿有女,偏又特别疼惜儿子,对闺女无限索取,导致儿子长大后自私自利,女儿心中愤愤不平,谁都不想履行赡养父母的义务。每每看到此类新闻 ,无比痛心。根深蒂固的封建思想,重男轻女在我们大中华,特别是农村以及偏远地区,无比猖獗。就拿家族族谱来说,许多姓氏追根溯源,都是从很久很久以前传承下来的。怎么传承呢?就是记入族谱。当然,如果记入族谱后,有人做了伤风败俗违反族规的事情,那么就要从族谱中除名了。很小的时候,我妈妈说,我们章氏也是有族谱的家族,族谱只记录可以传宗接代的男丁,可以延续章家香火,闺女那是没有资格入族谱的,因为闺女迟早要嫁人,是”别人家的“,我弟弟呢早就入了章家族谱,那一辈刚好是”茂“字辈,所以取名叫”章茂普“,当然这只是入族谱的名字,我弟弟早就改名成家立业了。。。

   那么就来说一说这个章氏族谱,我翻了翻家族辈分排行,将前几代的族谱大概整理如下:

  

Java 开发族谱 java实验家族的姓氏_组合模式

相信大家对这个族谱不陌生,这是一个典型的树状结构,我们今天的任务就是要把这个树状结构实现出来,并且还要让它可以自上而下,自下而上地遍历,那么就来稍作分析。从这个树状结构上看,有两种节点:有分支的节点(蓝色节点)和无分支的节点(橙色节点),它们的区别就是蓝色节点有子嗣,可以延续香火,而橙色节点很不幸,没有下一代,不能完成家族伟大的使命。这两种节点除了是否有子嗣问题,其他的属性,不如名字啊,第几代啊,父亲啊,都是一样的。那么我们是否可以将相同的信息提取出来,统一放到抽象类中,然后让这两种节点继承此类,另外给有子嗣的节点添加额外的子嗣遍历等方法,按照这个思路,看我的设计图:

   

Java 开发族谱 java实验家族的姓氏_java设计模式_02

 

这个呢,就是组合模式。

组合模式就是将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。

  组合模式的优点有哪些呢?第一个优点只要是树形结构,就要考虑使用组合模式,这个一定记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧。

下面我们就来实现上述的“章氏族谱”。首先,我们定义一个统一的抽象父类:


package com.pattern.composite;
/**
 * 章氏家族族谱
 * @author 
 *
 */
public abstract class Family {
  protected String name;//姓名
  protected int depth;//第几代
  protected Family parent;//父亲是谁
 
  public String getName() {
	return name;
  }

  public int getDepth() {
	return depth;
  }
  
  public void setParent(Family parent) {
	this.parent = parent;
  }

  public Family getParent() {
	return parent;
  }

  public Family(String name, int depth) {
	this.name = name;
	this.depth = depth;
   }
}

      由于子节点有两种,一种是有后代的节点,一种是没有后代的节点。我们将两者分别实现:


package com.pattern.composite;

import java.util.ArrayList;
import java.util.List;
/**
 * 有下一代的子族
 * @author 
 *
 */
public class BranchFamily extends Family {
	private List<Family> familyChildren=new ArrayList<Family>();//子族族谱,延续香火,生生不息

	public BranchFamily(String name, int depth) {
		super(name, depth);
	}
	public void addChild(Family famlily) {//将儿子们记入族谱
		familyChildren.add(famlily);
		famlily.setParent(this);
	}

	public void remove(Family famlily) {//做了伤风败俗的事,从族谱中除名 
		familyChildren.remove(famlily);
	}

	public List<Family> getChildren() {//看看都有哪些儿子啊
		return familyChildren;
	}
}
package com.pattern.composite;
/**
 * 没有下一代的子族
 * @author 
 *
 */
public class LeafFamily extends Family{

	public LeafFamily(String name, int depth) {
		super(name, depth);
	}
}

        组合模式,最主要的就是对树状结构迭代遍历。这里我们写测试类中,可以从根节点自上而下遍历,也可以给出任意子节点,自下而上遍历出所有的父节点。测试类如下:


package com.pattern.composite;

import java.util.List;

public class Test {
  public static void main(String[] args){
	  BranchFamily f1=new BranchFamily("章积才",1);//章氏第一代族谱  老祖宗
	  BranchFamily f20=new BranchFamily("章德华",2);//章氏第二代族谱
	  f1.addChild(f20);//设立父子关系
	  LeafFamily f21=new LeafFamily("章德渊",2);//章氏第二代族谱 无后
	  f1.addChild(f21);
	  BranchFamily f22=new BranchFamily("章德扬",2);//章氏第二代族谱
	  f1.addChild(f22);
	  BranchFamily f30=new BranchFamily("章嗣元",3);//章氏第三代族谱
	  f20.addChild(f30);
	  BranchFamily f31=new BranchFamily("章嗣繁",3);//章氏第三代族谱
	  f20.addChild(f31);
	  LeafFamily f32=new LeafFamily("章嗣义",3);//章氏第三代族谱 无后
	  f22.addChild(f32);
	  LeafFamily f33=new LeafFamily("章嗣充",3);//章氏第三代族谱 无后
	  f22.addChild(f33);
	  BranchFamily f34=new BranchFamily("章嗣戴",3);//章氏第三代族谱
	  f22.addChild(f34);
	  BranchFamily f35=new BranchFamily("章嗣卿",3);//章氏第三代族谱
	  f22.addChild(f35);
	  LeafFamily f40=new LeafFamily("章荣华",4);//章氏第四代族谱 无后
	  f30.addChild(f40);
	  LeafFamily f41=new LeafFamily("章荣兴",4);//章氏第四代族谱 无后
	  f31.addChild(f41);
	  BranchFamily f42=new BranchFamily("章荣盛",4);//章氏第四代族谱
	  f34.addChild(f42);
	  BranchFamily f43=new BranchFamily("章荣宣",4);//章氏第四代族谱
	  f34.addChild(f43);
	  LeafFamily f44=new LeafFamily("章荣家",4);//章氏第四代族谱 无后
	  f35.addChild(f44);
	  BranchFamily f45=new BranchFamily("章荣起",4);//章氏第四代族谱
	  f35.addChild(f45);
	  System.out.println("----------从根节点向下遍历------------------");
	  System.out.println(getFamilyChildrenInfo(f1));//从祖宗开始向下找,找出族谱的所有信息
	  System.out.println("----------从任意节点向上遍历------------------");
	  System.out.println(getFamilyParentInfo(f42));//给出族谱任意一人(这里是章荣盛),向上找出他所有的父族信息
  }
  /**
   * 从根节点向下遍历:找出所有的子族信息
   * @param root
   * @return
   */
  public static String getFamilyChildrenInfo(BranchFamily root){
	  List<Family> familyChildren=root.getChildren();
	  String info="";
	  for(Family f:familyChildren){
		  if(f instanceof LeafFamily){
			  info=info+f.getName()+",章氏族谱第"+f.getDepth()+"代\n";
		  }else{
			  info=info+f.getName()+",章氏族谱第"+f.getDepth()+"代\n子族:"+getFamilyChildrenInfo((BranchFamily)f);
		  }
	  }
	  return info;
  }
  /**
   * 从任意子节点向上遍历:找出所有的父族信息
   * @param f
   * @return
   */
  public static String getFamilyParentInfo(Family f){
	  String info="";
	  if(f.getParent()==null){
		  info=info+f.getName()+",章氏族谱第"+f.getDepth()+"代,已经是祖宗啦!没有上一代\n";
	  }else{
		  info=info+f.getName()+",章氏族谱第"+f.getDepth()+"代,\n父族:"+f.getParent().getName()+",\n"+getFamilyParentInfo(f.getParent())+"\n";
	  }
	  return info;
  }
}

  运行结果如下:

Java 开发族谱 java实验家族的姓氏_java设计模式_03

  从上述Test类可以看出,组合模式有一个非常明显的缺点,如下:

LeafFamily f44=new LeafFamily("章荣家",4);//章氏第四代族谱 无后
BranchFamily f45=new BranchFamily("章荣起",4);//章氏第四代族谱

        发现什么问题了吗?直接使用了实现类!这个在面向接口编程上是很不恰当的,这个在使用的时候要考虑清楚。

  组合模式在项目中到处都有,比如电脑的文件系统,比如菜单导航,比如XML文件。。。


  

  所以组合模式的应用非常广泛,那么我们来总结下组合模式的构成要素:

 抽象构件角色(component):是组合中的对象的抽象类接口,在适当的情况下,实现所有类共有接口的默认行为,声明一个接口用于访问和管理Component子部件,用来管理所有的子对象。 

 树叶构件角色(Leaf):在组合树中表示叶节点对象,叶节点没有子节点。并在组合中定义图元对象的行为。

 树枝构件角色(Composite):定义有子部件的那些部件的行为,存储子部件,且在Component接口中实现与子部件有关的操作。

 客户角色(Client):通过component接口操纵组合部件的对象。