订单
订单在我们的日常生活中非常常见。有时候一个订单中会包含其他的订单,就像下面这样:
在order订单中包含了两个子订单和一瓶牛奶,两个子订单又分别有自己的内容。这样就形成了一个订单树,具体的商品是叶子节点,订单是非叶子节点。就像下面这样:
那么我们应该怎么处理这种树形结构呢?答案就是使用组合模式
组合模式
组合模式是一种结构型设计模式,你可以使用它将对象组合成树状结构,并且能够像使用独立对象一样使用它们,所以组合模式又叫“部分整体模式”。
代码
接下来我们用代码来感受一下组合模式。
定义顶层接口
首先,我们先定义最顶层的接口。我把它成为“CalculateAble”,意思是可以被计算价值的东西。在后面的代码中,任何东西都是可以被计算价值的。
/**
* @author skyline
*/
public interface CalculateAble {
/**
* 计算价格
*/
double calculate();
/**
* 当前商品名
* @return
*/
String name();
/**
* 添加另一个可计算的节点
* @param calculateAble
*/
default void add(CalculateAble calculateAble) {}
/**
* 获得商品列表
* @return
*/
default List<CalculateAble> getCalculateAbleList() {
return null;
}
}
一些具体的商品
接下来我们定义一些具体的商品,这些商品是树形的叶子节点。
苹果
public class Apple implements CalculateAble {
@Override
public double calculate() {
return 20;
}
@Override
public String name() {
return "apple";
}
}
笔记本
public class AlienWareNoteBook implements CalculateAble {
@Override
public double calculate() {
return 25000;
}
@Override
public String name() {
return "alienWareNoteBook";
}
}
牛奶
public class Milk implements CalculateAble {
@Override
public double calculate() {
return 25;
}
@Override
public String name() {
return "Milk";
}
}
茶叶
public class Tea implements CalculateAble {
@Override
public double calculate() {
return 99;
}
@Override
public String name() {
return "tea";
}
}
订单
一个订单中会有多个产品,所以订单对象是树形的非叶子节点
public class Order implements CalculateAble {
private final List<CalculateAble> calculateAbles;
private final String name;
public Order(String name) {
this.calculateAbles = new ArrayList<>();
this.name = name;
}
@Override
public double calculate() {
//订单的总价格等订单内包含的所有物品的价格之和
double sum = 0;
for (CalculateAble calculateAble : calculateAbles) {
sum += calculateAble.calculate();
}
return sum;
}
@Override
public String name() {
return name;
}
@Override
public void add(CalculateAble calculateAble) {
calculateAbles.add(calculateAble);
}
@Override
public List<CalculateAble> getCalculateAbleList() {
return calculateAbles;
}
}
main
最后,我们看下main方法
public class CompositeMain {
public static void main(String[] args) {
//创建一个叫order的订单
CalculateAble order = new Order("order");
//创建一个叫smallOrder的订单
CalculateAble smallOrder = new Order("smallOrder");
//为smallOrder中添加一个商品:苹果
smallOrder.add(new Apple());
//为smallOrder中添加一个商品:茶叶
smallOrder.add(new Tea());
//创建一个叫bigOrder的订单
CalculateAble bigOrder = new Order("bigOrder");
//为bigOrder中添加一个商品:笔记本
bigOrder.add(new AlienWareNoteBook());
//将smallOrder添加到order中
order.add(smallOrder);
//将bigOrder添加到order中
order.add(bigOrder);
//将牛奶添加到order中
order.add(new Milk());
//递归打印树形
loop(order, 0);
//计算总价
double calculate = order.calculate();
System.out.println("总价为:" + calculate);
}
private static void loop(CalculateAble calculateAble, int level) {
if (level == 0) {
} else if (level == 1) {
System.out.print("├─");
} else {
for (int i = 0; i < level - 1; i++) {
System.out.print("│ ");
}
System.out.print("├─");
}
System.out.println(calculateAble.name());
List<CalculateAble> calculateAbleList = calculateAble.getCalculateAbleList();
if (calculateAbleList != null) {
for (CalculateAble able : calculateAbleList) {
loop(able, level + 1);
}
}
}
}
最后,我们看看执行结果:
从结果中看到,程序打印出的树形与我们在一开始画的树形是一样的。总价的计算也没有问题。
总结
- 如果你要处理树形结构的数据,那你可以考虑组合模式
- 如果你希望可以以相同的方式处理简单对象(
Apple
、Tea
、AlienWareNoteBook
、Milk
)与复杂对象(Order
)也可以使用组合模式