java设计模式之组合模式

               

<作用>

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

<结构图>


涉及角色:

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.公司内各部门的层级关系


           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值