使用Composite Pattern +Visitor Pattern 设计 商品分类

  商品分类树是电子商务网站比不可少的模块,设计一个基于内存的商品分类模块是比不可少的。

  模块由以下几类函数组成:

    1、商品数量的统计与更新。

    2、节点的常用操作:添加、删除、获取根节点,叶子判断。

    3、商品种类遍历。

    4、商品种类检索。

    5、商品分类树的构建。

  

  相关注释已经写在代码,代码如下:

package com.ming.article.pattern.commodity;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;

/**
* 商品分类
*
@author martin.chen
*
*/
public class CommodityClassify implements Cloneable {
private static final int DEFAULT_AMOUNT = 0;
//************************* 节点属性 ******************************
/**
* 分类名称
*/
private String name;
/**
* 商品分类码
*/
private String code;
/**
* 分类下种类数
*/
private int amount ;
/**
* 是否要计算商品种类
*/
private boolean needRecount = true;

//*************************** 树形关系 *********************************
/**
* 双亲结点
*/
private CommodityClassify parent;
/**
* 孩子结点
*/
private List<CommodityClassify> children = Collections.synchronizedList(new LinkedList<CommodityClassify>());

//*************************** 构造函数 *********************************

public CommodityClassify(String name, String code) {
this(name, code, DEFAULT_AMOUNT);
}

public CommodityClassify(String name, String code, int amount) {
this.name = name;
this.code = code;
this.amount = amount;
}
//*************************** 商品数量的统计与更新 *********************************

/**
* 更新商品种类数量
* 只有叶子节点才能更新数量
*
@param amount
*/
public void updateAmount(int amount) {
//1、如果是叶子节点,则传入amount,否则跳过
if (isLeafy() && this.amount != amount) {
this.amount = amount;
onAmountChange();
}
}
/**
* 返回树的深度
*
@return
*/
public int getDepth() {
int depth = 1;
CommodityClassify root = parent;
while (root != null) {
depth += 1;
root = root.getParent();

}
return depth;
}

/**
* 商品分类的数量变化时,重新计算
* 1、通知过程要沿着双亲往上通知,直到根节点。
* 2、数量的更新从根节点开始,递归更新
*/
private void onAmountChange() {
notifyParentChange();
recountTotal();
}

/**
* 重新计算 商品 总数
*/
private void recountTotal() {
this.getRoot().calcAmount();
}
/**
* 计算当前节点的商品种类
*
*/
private int calcAmount() {
if (needRecount && !isLeafy()) {
amount = DEFAULT_AMOUNT;
for (CommodityClassify cc : children) {
amount += cc.calcAmount();
}
needRecount = false;
}
return amount;
}

/**
* 通知双亲结点:数据需要重新计算
*/
private void notifyParentChange() {
this.setNeedRecount(true);
CommodityClassify parents = this.parent;
while (parents != null) {
parents.setNeedRecount(true);
parents = parents.parent;
}
}

//********************* 节点的常用操作:添加、删除、获取根节点,叶子判断 **********************

/**
* 增加孩子结点
* 需要同步
*
@param cc
*/
private void addChild(CommodityClassify cc) {
//1、设置双亲节点
cc.setParent(this);
//2、增加叶子节点
children.add(cc);
onAmountChange();
}
/**
* 删除孩子节点
*
*
@param cc
*/
private void removeChild(CommodityClassify cc) {
//1、待删除的节点清空双亲
cc.setParent(null);
//2、删除节点
children.remove(cc);
//3、通知变化
onAmountChange();
}
/**
* 返回根节点
*
*
@return
*/
private CommodityClassify getRoot() {
CommodityClassify root = this;
while (root.parent != null) {
root = root.parent;
}
return root;
}

/**
* 判断是否为叶子节点
*/
private boolean isLeafy() {
return children.isEmpty();
}


//************************* 商品种类遍历 **************************

/**
* 广度优先遍历
*/
public void breadthFirstTraversal(CommodityVisitor visitor) {
Queue<CommodityClassify> queue = new LinkedList<CommodityClassify>();
queue.add(this);
while (!queue.isEmpty()) {
CommodityClassify c = queue.poll();
visitor.visit(c);
queue.addAll(c.getChildren());
}
}

/**
* 返回层次遍历的的链表
*
*
@return
*/
public List<CommodityClassify> getBFTList() {
List<CommodityClassify> list = new LinkedList<CommodityClassify>();
int currentIndex = 0;
list.add(this);
while (currentIndex < list.size()) {
if (!list.get(currentIndex).isLeafy()) {
list.addAll(list.get(currentIndex).getChildren()); ///将子节点添加的list中
}
currentIndex++;
}
return list;
}

/**
* 深度优先遍历 非递归算法
*/
public void depthFirstTraversal(CommodityVisitor visitor) {
Stack<CommodityClassify> stack = new Stack<CommodityClassify>();
stack.push(this);
while (!stack.isEmpty()) {
CommodityClassify c = stack.pop();
visitor.visit(c);
if (!c.isLeafy()) {
stack.addAll(c.getChildren());
}
}
}

//************************* 商品种类检索 **************************

/**
* 广度优先检索
*/
public CommodityClassify breadthFirstSearch(CommoditySearcher searcher) {
Queue<CommodityClassify> queue = new LinkedList<CommodityClassify>();
queue.add(this);
while (!queue.isEmpty()) {
CommodityClassify c = queue.poll();
if (searcher.search(c)) {
return c;
}
queue.addAll(c.getChildren());
}
return null;
}

/**
* 深度优先检索
*/
public CommodityClassify depthFirstSearch(CommoditySearcher searcher) {
Stack<CommodityClassify> stack = new Stack<CommodityClassify>();
stack.push(this);
while (!stack.isEmpty()) {
CommodityClassify c = stack.pop();
if (searcher.search(c)) {
return c;
}
if (!c.isLeafy()) {
stack.addAll(c.getChildren());
}
}
return null;
}


//*************************** 商品分类树的构建*******************************
/**
* 添加子商品分类
*
@param name 分类名称
*
@param code 分类编码
*
@param amount 数量
*
@return
*/
public CommodityClassify addChild(String name, String code, int amount) {
CommodityClassify c = new CommodityClassify(name, code, amount);
addChild(c);
return c;
}

public CommodityClassify addChild(String name, String code) {
CommodityClassify c = new CommodityClassify(name, code);
addChild(c);
return c;
}

public CommodityClassify getByCode(final String code) {
CommoditySearcher vs = new CommoditySearcher() {
public boolean search(CommodityClassify c) {
return (c.getCode().equals(code));
}
};
return depthFirstSearch(vs);
}
/**
* 把 当前树 从 整棵树中 剥离出来;
*/
public void remove() throws Exception {
if (this.getParent() == null) throw new Exception("根节点不能删除");
//1、找到双亲
parent = this.getParent();
//2、删除当前树
parent.removeChild(this);
}

/**
* 删除商品分类
*
@param cc
*
@throws Exception
*/
public void remove(CommodityClassify cc) throws Exception {
if (this.getParent() == null) throw new Exception("根节点不能删除");
if (this.equals(cc)) throw new Exception("不能删除自身");
if (!this.children.contains(cc)) throw new Exception("不包含子节点");
removeChild(cc);
}


//************************ 其他 ********************************************
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj.getClass() != this.getClass()) return false;
CommodityClassify c = (CommodityClassify) obj;
return c.getCode().equals(this.getCode());
}

public Object clone() throws CloneNotSupportedException {
super.clone();
CommodityClassify c = new CommodityClassify(name, code, amount);
List<CommodityClassify> ccList = Collections.synchronizedList(new LinkedList<CommodityClassify>());
for (CommodityClassify cc : children) {
CommodityClassify newCC = (CommodityClassify) cc.clone();
newCC.setParent(c);
ccList.add(newCC);
}

c.setChildren(ccList);
return c;
}

//************************* getter/setter ******************************
protected void setChildren(List<CommodityClassify> children) {
this.children = children;
}

private void setNeedRecount(boolean needRecount) {
this.needRecount = needRecount;
}

public CommodityClassify getParent() {
return parent;
}

public void setParent(CommodityClassify parent) {
this.parent = parent;
}

public String getName() {
return name;
}

public String getCode() {
return code;
}

public List<CommodityClassify> getChildren() {
return children;
}

public int getAmount() {
return amount;
}
}

 

package com.ming.article.pattern.commodity;
public interface CommoditySearcher {
public boolean search(CommodityClassify c);
}

 

package com.ming.article.pattern.commodity;
public interface CommodityVisitor {
public void visit(CommodityClassify cc);
}





 

posted on 2011-12-25 22:04  small.ming  阅读(384)  评论(0编辑  收藏  举报

导航