问题
在双11大促中,为了提高销量,我决定将华为电脑,手机,耳机等系列商品做成礼盒打包销售,初步暂定了两个礼盒:
1. 华为高级礼盒:华为笔记本 + 机械键盘 + 手机礼盒,手机礼盒:手机 + 耳机 + 手表礼盒, 手表礼盒:手表 + 定制表带
2. 华为基础礼盒:华为笔记本 + 无线鼠标 + 手机礼盒,手机礼盒:手机 + 手表礼盒,手表礼盒:手表
销售员问我这两个礼盒如何定价,我告诉他说,把所有商品的价格加起来,打个九折就行。销售员拿着计算器吭哧吭哧算了半天,告诉我高级礼盒价值¥18000,基础礼盒价值¥12000,我不放心,又让另一个销售员算了一遍,算出来高级礼盒价值¥18500,基础礼盒价值¥12600。我一听这可不行啊,打包算个礼盒的钱,这么容易出错。那我得用个设计模式来实现,保证不能出错啊。这就引出了我们本次的主题——组合模式。
概述
使用组合模式有两个很必要的前提,第一就是核心模型可以应用为树形结构,第二就是树形结构的节点有通用属性。就比如二叉树,分为叶子节点和非叶子节点,有一个共同的属性值;比如文件夹和文件,文件夹可包含文件夹或文件,而其都有一个路径,可用来索引或保存等;再比如说我们这里的礼盒套装,礼盒可包含礼盒,也可以包含物品,并且都有一个共同的属性——价格。
实现
定义父类,设置通用属性—价格:
public abstract class Component {
protected int price;
public Component(int price) {
this.price = price;
}
/**
* 计算价格
*
* @return 价格
*/
abstract int calculate();
}
定义礼盒:
public class Container extends Component {
private List<Component> componentList = new ArrayList<>();
public Container(int price) {
super(price);
}
@Override
public int calculate() {
for (Component component: componentList) {
price += component.calculate();
}
return price;
}
public void add(Component component) {
componentList.add(component);
}
public void remove(Component component) {
componentList.remove(component);
}
}
定义商品(为了方便,把商品定义简化):
public class Computer extends Component {
public Computer(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
public class HeadSet extends Component{
public HeadSet(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
public class Keyboard extends Component {
public Keyboard(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
public class MobilePhone extends Component {
public MobilePhone(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
public class Mouse extends Component{
public Mouse(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
public class Watch extends Component {
public Watch(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
public class WatchBand extends Component {
public WatchBand(int price) {
super(price);
}
@Override
public int calculate() {
return price;
}
}
计算礼盒价格:
public static void main(String[] args) {
// calculate high container
//container can be free can be not free
Container highContainer = new Container(0);
Computer computer = new Computer(10000);
Keyboard keyboard = new Keyboard(1500);
Container mobileContainer = new Container(0);
MobilePhone mobilePhone = new MobilePhone(6000);
HeadSet headSet = new HeadSet(1300);
Container watchContainer = new Container(0);
Watch watch = new Watch(2000);
WatchBand watchBand = new WatchBand(300);
highContainer.add(computer);
highContainer.add(keyboard);
highContainer.add(mobileContainer);
mobileContainer.add(mobilePhone);
mobileContainer.add(headSet);
mobileContainer.add(watchContainer);
watchContainer.add(watch);
watchContainer.add(watchBand);
System.out.println("高级礼盒价格: " + highContainer.calculate());
Container lowContainer = new Container(0);
lowContainer.add(computer);
Mouse mouse = new Mouse(800);
lowContainer.add(mouse);
Container lowMobileContainer = new Container(0);
Container lowWatchContainer = new Container(0);
lowWatchContainer.add(watch);
lowMobileContainer.add(mobilePhone);
lowMobileContainer.add(lowWatchContainer);
lowContainer.add(lowMobileContainer);
System.out.println("基础礼盒价格: " + lowContainer.calculate());
}
计算结果:
高级礼盒价格: 21100
基础礼盒价格: 18800
总结
从上述的计算过程中,可以大概理解组合模式的思想以及想解决的问题。其是为了解决组合对象的复杂性,我们将组合对象看做整体,我们把对组合对象的操作简化为对整体的操作,而无需关心内部的操作流程,将调用方和内部对象进行解耦。优点:可自由添加节点:我可以轻易的在礼盒中添加物品或删除物品,调用简单:无论我如何添加或删除物品,都不影响计算总价格。
在实现组合模式的过程中,思考到一个问题,可能也是组合模式的缺点,那就是Component作为抽象类,所有需要计算价格的子类都需继承,而这可能是在系统的后期加入的,那么类似于Computer,MobilePhone等早已有抽象父类,那么对其的改造可能需要在对应抽象父类上再抽象一层Component,由各个抽象父类继承。在调整时会有一些困难,以及难以理解。