java设计模式之组合模式

<作用>

将对象组合成树形结构以表示“部分-整体”的层次结构。
组合模式使得用户对单个对象和组合对象的使用具有唯一性

<结构图>

java设计模式之组合模式_第1张图片

涉及角色:

1.Component 是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
2.Leaf 在组合中表示叶子结点对象,叶子结点没有子结点。
3.Composite 定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
<实际项目运用>

最近在做基于Mondrin的olap统计分析时,需要事先在xml文件中定义好mdx语言查询语句,而且该mdx语言查询语句还是树形结构,所以想到使用组合模式来配合完成。具体代码如下:

XML文件格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<mondrain type="mon">
	<!-- 配置报表类型的MDX语句 配置方式:地区($area) 、年份($year) 、月份($month) type表示数据类型,contentData:内容数据、titleData:头部数据、comb0~9:组合数据 -->
	<report type="NewActiveUser" name="新增活跃数">
		<title>
			<param type="T" name="新增活跃数的表头">select {[地区].[$area].Children} ON
				columns,{{[时间].[$year]}*{[Measures].[到达目标]}} ON rows from
				[ACTIVE_GOAL]</param>
		</title>

		<content>
			<param type="C" name="新增活跃数据">WITH MEMBER [Measures].[同比新增] AS
				'([Measures].[新增活跃用户]-ParAllelPeriod(Year,1))/ParAllelPeriod(Year,1)',FORMAT_STRING='##.00%'
				MEMBER [Measures].[年度到达] AS
				'Measures.[年度到达].Parent+[Measures].[新增活跃用户]'select
				{[地区].[$area].Children} ON columns,
				{{[时间].[$year].Children}*{[Measures].[新增活跃用户],[Measures].[同比新增],[Measures].[年度到达]}}
				ON rows from [Active] </param>
		</content>

	</report>

	<report type="deal" name="交易额">
		<title>
			<param type="T" name="交易额目标">WITH MEMBER [地区].[$area].[合计] AS
				'Sum([地区].[$area].Children)'select
				AddCalculatedMembers([地区].[$area].Children) ON
				columns,{{[时间].[$year]}*{[Measures].[到达数目标值]}}ON rows from
				[Active_Goal] where [发展目标].[101138].[10113804]</param>
		</title>
		<content>
			<param type="C" name="组合数据">
				<parm type="L" name="交易额查询不带个人消费 ">
					WITH MEMBER [地区].[$area].[合计] AS 'Sum([地区].[$area].Children)'select
					AddCalculatedMembers([地区].[$area].Children) ON columns,
					{{[时间].[$year].[$month]}*{[Measures].[控制台充值金额],[Measures].[POS充值金额],[Measures].[网上充值金额],[Measures].[短信充值金额],[Measures].[IVR充值金额],[Measures].[OTA充值金额],[Measures].[WAP充值金额],[Measures].[营业厅充值金额],[Measures].[客户端充值金额],[Measures].[代扣充值金额],[Measures].[TSM充值金额],[Measures].[IPTV充值金额],[Measures].[添益宝充值金额],[Measures].[手机收银台充值金额],[Measures].[控制台消费金额],[Measures].[POS消费金额],[Measures].[网上消费金额],[Measures].[短信消费金额],[Measures].[IVR消费金额],[Measures].[客户端消费金额],[Measures].[代扣消费金额],[Measures].[TSM消费金额],[Measures].[IPTV消费金额],[Measures].[OTA消费金额],[Measures].[WAP消费金额],[Measures].[营业厅消费金额],[Measures].[添益宝消费金额],[Measures].[手机收银台消费金额]}}
					ON rows from [deal_cube]
				</parm>
				<parm type="L" name="交易汇总">
					WITH MEMBER [地区].[$area].[合计] AS [地区].[$area].Parent select
					AddCalculatedMembers([地区].[$area].Children) ON columns,
					{{[时间].[$year].[$month]}*{[Measures].[企业账户资金归集],[Measures].[网关交易],[Measures].[积分],[Measures].[营业厅POS收单]}}
					ON rows from [deal_total_cube]
				</parm>
			</param>
		</content>
	</report>

	<report type="AccountNum" name="账户数">
		<title>
			<param type="T" name="账户数表头">WITH MEMBER [地区].[$area].[合计] AS
				'Sum([地区].[$area].Children)'MEMBER [Measures].[上年到达数目标值] AS
				'Sum({ParAllelPeriod(Year,1,[时间].[$year].[12])}*{[Measures].[年度到达]})'MEMBER
				[Measures].[净增目标] AS '[Measures].[到达目标]-[Measures].[上年到达数目标值]'select
				AddCalculatedMembers([地区].[$area].Children) ON
				columns,{{[时间].[$year]}*{[Measures].[到达目标],[Measures].[净增目标]}}ON
				rows from [ACCOUNT_NUM_GOAL]</param>
		</title>

		<content>
			<param type="C" name="账户数数据">WITH MEMBER [Measures].[净增开户] AS
				'([Measures].[本月新增开户数]-[Measures].[销户数])' MEMBER [地区].[$area].[合计]
				AS 'Sum([地区].[$area].Children)'select
				AddCalculatedMembers([地区].[$area].Children) ON columns,
				{{[时间].[$year].[$month]}*{[Measures].[净增开户],[Measures].[年度到达]}} ON
				rows from [ACCOUNT_NUM]</param>
		</content>

	</report>
		
		<report type="businesspos" name="商户POS交易">
			<title >
				<param type="T" name="POS商户交易情况" >WITH MEMBER [地区].[$area].[合计] AS 'Sum([地区].[$area].Children)'select AddCalculatedMembers([地区].[$area].Children) ON columns,{{[时间].[$year]}*{[Measures].[到达数目标值]}}ON rows from [Active_Goal] where [发展目标].[101138].[10113804]</param>
			</title>
		</report>
</mondrain>

Component类:

package cn.my.da.service.olap.DataCollection.readconfig;
/**
 * 组合对象
 * @author xiaowen
 * @date 2016年5月26日
 * @ version 1.0
 */
public abstract class Component {
	
	/**
	 * 节点名字
	 */
	protected String  nodeName;
	
	public Component(){};
	/**
	 * 带参数的构造方法
	 * @param nodeName
	 */
	public Component(String  nodeName){
		this.nodeName = nodeName;
	}
	/**
	 * 添加子节点
	 * @param component
	 */
	public  abstract void addChildren(Component component);
	/**
	 * 删除子节点
	 * @param component
	 */
	public  abstract void removeChildren(Component component);
	
	
	/**
	 * 是否还有下一个子节点
	 * boolean
	 */
	public abstract  boolean hasNextChlidNode();
		

}

Leaf类:

package cn.my.da.service.olap.DataCollection.readconfig;
/**
 * 叶节点对象。叶子节点没有子节点
 * @author xiaowen
 * @date 2016年5月26日
 * @ version 1.0
 */
public class Leaf extends Component {

	public Leaf(String nodeName) {
		super(nodeName);
	}

	@Override
	public void addChildren(Component component) {
		System.out.println("Can not add to a leaf");
	}

	@Override
	public void removeChildren(Component component) {
		System.out.println("Can not remove from a leaf");
	}

	@Override
	public boolean hasNextChlidNode() {
		return false;
	}

}
Composite类:
package cn.my.da.service.olap.DataCollection.readconfig;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 定义枝节点的行为
 * @author xiaowen
 * @date 2016年5月26日
 * @ version 1.0
 */
public class Composite extends Component {

	/**
	 * 存储子节点集合
	 */
	private List<Component>  children = new ArrayList<Component>();
	
	/**
	 * 存储节点数据
	 */
	private Map<String, String> nodeAttribute;
	/**
	 * 不带参数的构造方法
	 */
	public Composite(){};
	/**
	 * 带参数的构造方法
	 * @param nodeName
	 * @param nodeAttribute
	 */
	public Composite(String nodeName,Map<String, String> nodeAttribute) {
		super(nodeName);
		this.nodeAttribute = nodeAttribute;
		
	}
    
	public Composite(String nodeName, List<Component> children, Map<String, String> nodeAttribute) {
		super(nodeName);
		this.children = children;
		this.nodeAttribute = nodeAttribute;
	}

	@Override
	public void addChildren(Component component) {
		this.children.add(component);		
	}

	@Override
	public void removeChildren(Component component) {
		this.children.remove(component);
	}

	@Override
	public boolean hasNextChlidNode() {
	return children.isEmpty()&&children.size()==0?true:false;
	}

	/**
	 * @return the children
	 */
	public List<Component> getChildren() {
		return children;
	}

	/**
	 * @param children the children to set
	 */
	public void setChildren(List<Component> children) {
		this.children = children;
	}

	/**
	 * @return the nodeAttribute
	 */
	public Map<String, String> getNodeAttribute() {
		return nodeAttribute;
	}

	/**
	 * @param nodeAttribute the nodeAttribute to set
	 */
	public void setNodeAttribute(Map<String, String> nodeAttribute) {
		this.nodeAttribute = nodeAttribute;
	}

}

读取xml配置文件并将读取的数据存储在组合对象中:

读取配置文件的接口IReadConfig

package cn.my.da.service.olap.DataCollection.readconfig;
/**
 * 读取配置文件的接口
 * @author xiaowen
 * @date 2016年5月26日
 * @ version 1.0
 */
public interface IReadConfig {


	/**
	 * 初始化配置文件
	 * @param configName
	 * @return 
	 * Component
	 */
	public Component   inintConfigByName(String configName);
	/**
	 * 根据节点名获取对象
	 * @param configNodeName 节点名称 
	 * @return 
	 * Component
	 */
	public Component getComponent(String configNodeName);
}
读取配置文件的实现接口IReadConfigImpl:

package cn.my.da.service.olap.DataCollection.readconfig;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import cn.my.da.service.olap.DataCollection.utils.StaticConstant;

/**
 * 读取配置文件的实现类
 * @author xiaowen
 * @date 2016年5月26日
 * @ version 1.0
 */
public class IReadConfigImpl  implements IReadConfig{

	/**
	 * 组合对象
	 */
	private Composite  composite;
	/**
	 * 存储title和content数据
	 */
	private Map<String, Composite> map = new LinkedHashMap<String, Composite>();

	
	@Override
	public Component inintConfigByName(String configName) {
		try {
			// 创建解析xml配置文件的SAX对象,基于事件驱动机制解析xml
			SAXReader reader = new SAXReader();
			LoggerUtils.info("XML文件《" + configName + "》读取开始......");
			String path = ReadConfigManagerImpl.class.getClassLoader()
					.getResource(configName).getPath();
			// 开始读取配置文件
			Document doc = reader.read(new File(path));
			//创建组合对象
			composite = new Composite();
			// 获取根节点
			Element rootElem = doc.getRootElement();
			composite.setNodeName(rootElem.getName());
           //循环存储xml数据结构
			for (Iterator i = rootElem.elementIterator(); i.hasNext();) {
				Element reportElem = (Element) i.next();
				Composite report = new Composite();
				report.setNodeName(reportElem.getName());
				report.setNodeAttribute(setConfigMapValue(reportElem));
				composite.addChildren(report);
				for (Iterator j = reportElem.elementIterator(); j.hasNext();) {
					Element childElem = (Element) j.next();
					Composite child = new Composite();
					child.setNodeName(childElem.getName());
					child.setNodeAttribute(setConfigMapValue(childElem));
					report.addChildren(child);
					//节点的名称
					String nodeName = child.getChildren().get(0).nodeName;
					if (nodeName.equals(StaticConstant.CONTENT_NAME)) {//内容部分
						map.put(StaticConstant.CONTENT_NAME
								+ StaticConstant.LOWER_LINE
								+ report.getNodeAttribute().get(StaticConstant.NODE_TYPE), child);
					} else if (nodeName.equals(StaticConstant.TITLE_NAME)) {//表头部分
						map.put(StaticConstant.TITLE_NAME
								+ StaticConstant.LOWER_LINE
								+ report.getNodeAttribute().get(StaticConstant.NODE_TYPE), child);
					}
					//循环子节点是否还有一个元素
					for (Iterator g = childElem.elementIterator(); g.hasNext();) {
						Element gElem = (Element) g.next();
						getElementList(gElem, child);
					}
				}
			}
		} catch (DocumentException e) {
			e.printStackTrace();
			throw new ConfigException("读取《" + configName + "》文件失败,错误信息:" + e);
		}
		return composite;
	}

	@Override
	public Component getComponent(String configNodeName) {
		return map.get(configNodeName)!=null?map.get(configNodeName):null;
	}

	/**
	 * 递归遍历元素
	 * 
	 * @param element
	 * @param configvo
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected void getElementList(Element element, Composite composite) {
		Composite c1 = new Composite(element.getName());
		c1.setNodeAttribute(setConfigMapValue(element));
		composite.addChildren(c1);
		// 获取所有的节点
		List<Element> elements = element.elements();
		if (elements.size() == 0) {//递归临界点
			System.out.println("已没有任何的元素!!!!");
		} else {
			// 有子元素
			for (Iterator it = elements.iterator(); it.hasNext();) {
				Element elem = (Element) it.next();
				getElementList(elem, c1); // 递归遍历

			}
		}
	}
	/**
	 * 设置Map值
	 * 
	 * @param elem
	 * @return Map<String,String>
	 */
	@SuppressWarnings("unchecked")
	private Map<String, String> setConfigMapValue(Element elem) {
		Map<String, String> map = new HashMap<String, String>();
		map.put(StaticConstant.ROOT_NAME, elem.getName() != null ? elem.getName() : null);
		if (elem.getText() != null) {
			map.put(StaticConstant.GAIN_DATA_NAME, elem.getTextTrim());
		}
		List<Attribute> at = elem.attributes();
		for (int i = 0; i < at.size(); i++) {
			Attribute item = at.get(i);
			map.put(item.getName(), item.getValue());

		}
		return map;

	}

}

另附工具类代码:

LoggerUtils:

package cn.my.da.service.olap.DataCollection.utils;
/**
 * 日志类
 * @author wangbowen
 * @version 1.0
 */
public class LoggerUtils {
    /**
     * log对象
     */
    private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getRootLogger();
    /**
     * 日志信息
     * @param message 输出信息
     */
    final public static void info(Object message) {
        log.info(message);
    }
    /**
     * 调试信息
     * @param message 输出信息
     */
    final public static void debug(Object message) {
        log.debug(message);
    }
    /**
     * 错误信息
     * @param message 输出信息
     */
    final public static void error(Object message) {
        log.error(message);
    }
    /**
     * 警告信息
     * @param message 输出信息
     */
    final public static void warn(Object message) {
        log.warn(message);
    }
    /**
     * 严重错误信息
     * @param message 输出信息
     */
    final public static void fatal(Object message) {
        log.fatal(message);
    }

}
StaticConstant静态变量类:
package cn.my.da.service.olap.DataCollection.utils;

/** 
 * 静态常量类
 * @author wangbowen 
 * @version 1.0
 */
public class StaticConstant {
    /**
     * 配置文件名称
     */
	public static final String CONFIG_NAME = "dataCollectionConfigInfo.xml";
	
	public static final String 	THREAD_CONFIG_NAME ="threadConfigInfo.xml";
	/**
	 * 获取Map数据的键名
	 */
	public static final String GAIN_DATA_NAME="data";
	/**
	 * 根节点名
	 */
	public static final String ROOT_NAME ="rootName";
	/**
	 * 父级名称
	 */
	public static final String  CONFIG_ROOT_NAME ="mon";
	/**
	 * 子级名称
	 */
	public static final String  CHILD_NODE_NAME ="threadSet";
	
	/**
	 * 标识符
	 */
	public static final String  WHIPPLE_TREE ="-";
	
	public static final String LOWER_LINE ="_";
	
	public static final String TITLE_NAME ="title";
	
	public static final String CONTENT_NAME ="content";
	
	public static final String NODE_TYPE ="type";
	/**
	 * 一月
	 */
	public static final String  JANUARY = "1";
	/**
	 * 读取数据线程bean名称
	 */
	public static final String READ_THREAD_NAME = "readDataThreadManager";
	/**
	 * 修改线程bean名称
	 */
	public static final String WRITE_THREAD_NAME="fileDataThreadManager";
	/**
	 * 读取配置文件bean名称
	 */
	public static final String  READ_CONFIGINFO_NAME="iReadConfigManager";
	/**
	 * 休眠时间
	 */
	public static final Long SLEEP_TIME=2500L;
	
	/**
	 * rmi访问地址
	 */
	public static final String RMI_ADDRESS = "rmi://192.168.4.167:8099/dataAnalysisQuery";
	
	public static final String LOCAL_ADDRESS="rmi://localhost:8099/dataAnalysisQuery";
	
	
}


<应用场景>

1.想表示对象的部分-整体层次结构

2.希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

<应用例子>

1.文件系统目录结构

2.网站导航结构

3.公司内各部门的层级关系


你可能感兴趣的:(java设计模式之组合模式)