本篇博文说的是组合模式
描述性文字
组合模式,又称为 部分整体模式,把具有相似的一组对象 当做一个对象处理,用一种树状的结构来组合对象,再提供统一的方法去访问相似的对象,以此忽略掉对象与对象容器间的差别。
举个栗子
注:此处直接使用的是原图。
假设这两类需求如下:
菜单:菜单名,描述信息,添加,添加删除子菜单或菜品 递归打印出所有的子菜单与菜品!
菜品:菜名,描述信息,价格,打印信息好的,先试试不用组合模式,要怎么写~
不使用组合模式写菜单
示例代码:
package structPattrn.compositePattern;
import java.util.ArrayList;
import java.util.List;
/**
* 组合模式测试例程
* @Package structPattrn.compositePattern
* @Title: CompositePatternDemo.java
* @Company: $
* @author BurgessLee
* @date 2018年10月25日-下午5:06:05
* @Description: $
*/
public class CompositePatternDemo {
public static void main(String[] args) {
//不用组合模式测试例程
Menu menu = new Menu("大菜单","包含所有的子菜单");
Menu drinkMenu = new Menu("饮品菜单","都是喝的");
Menu eatMenu = new Menu("小吃菜单","都是吃的");
MilkTea milkTea = new MilkTea("珍珠卖茶","珍珠+奶茶", 5);
Juice juice = new Juice("猕猴桃饮料","无添加剂",5);
HandCake handCake = new HandCake("咖喱鱼蛋","微辣",6);
FishBoll fishBoll = new FishBoll("培根手抓饼","正宗台湾风味",6);
drinkMenu.addMilkTea(milkTea);
drinkMenu.addJuice(juice);
eatMenu.addHandCake(handCake);
eatMenu.addFishBoll(fishBoll);
menu.addMenu(drinkMenu);
menu.addMenu(eatMenu);
System.out.println(menu.toString());
}
}
//以下部分代码使用的不是组合模式实现的==========================================================================
class MilkTea{
private String name;
private String desc;
private int price;
public MilkTea(String name, String desc, int price) {
super();
this.name = name;
this.desc = desc;
this.price = price;
}
@Override
public String toString() {
return "MilkTea [name=" + name + ", desc=" + desc + ", price=" + price
+ "]";
}
}
class Juice{
private String name;
private String desc;
private int price;
public Juice(String name, String desc, int price) {
super();
this.name = name;
this.desc = desc;
this.price = price;
}
@Override
public String toString() {
return "Juice [name=" + name + ", desc=" + desc + ", price=" + price
+ "]";
}
}
class HandCake{
private String name;
private String desc;
private int price;
public HandCake(String name, String desc, int price) {
super();
this.name = name;
this.desc = desc;
this.price = price;
}
@Override
public String toString() {
return "HandCake [name=" + name + ", desc=" + desc + ", price=" + price
+ "]";
}
}
class FishBoll{
private String name;
private String desc;
private int price;
public FishBoll(String name, String desc, int price) {
super();
this.name = name;
this.desc = desc;
this.price = price;
}
@Override
public String toString() {
return "FishBoll [name=" + name + ", price=" + price + "]";
}
}
class Menu{
private String name;
private String desc;
private List<Menu> menus = new ArrayList<>();
private List<MilkTea> milkTeas = new ArrayList<>();
private List<Juice> juices = new ArrayList<>();
private List<HandCake> handCakes = new ArrayList<>();
private List<FishBoll> fishBolls = new ArrayList<>();
public Menu(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
public void addMilkTea(MilkTea milkTea){
this.milkTeas.add(milkTea);
}
public void addJuice(Juice juice){
this.juices.add(juice);
}
public void addHandCake(HandCake handCake){
this.handCakes.add(handCake);
}
public void addFishBoll(FishBoll fishBoll){
this.fishBolls.add(fishBoll);
}
public void addMenu(Menu menu){
this.menus.add(menu);
}
@Override
public String toString() {
return "Menu [name=" + name + ", desc=" + desc + ", menus=" + menus
+ ", milkTeas=" + milkTeas + ", juices=" + juices
+ ", handCakes=" + handCakes + ", fishBolls=" + fishBolls + "]";
}
}
打印结果:
Menu [name=大菜单, desc=包含所有的子菜单, menus=[Menu [name=饮品菜单, desc=都是喝的, menus=[], milkTeas=[MilkTea [name=珍珠卖茶, desc=珍珠+奶茶, price=5]], juices=[Juice [name=猕猴桃饮料, desc=无添加剂, price=5]], handCakes=[], fishBolls=[]], Menu [name=小吃菜单, desc=都是吃的, menus=[], milkTeas=[], juices=[], handCakes=[HandCake [name=咖喱鱼蛋, desc=微辣, price=6]], fishBolls=[FishBoll [name=培根手抓饼, price=6]]]], milkTeas=[], juices=[], handCakes=[], fishBolls=[]]
使用组合模式写菜单
示例代码:
abstract class AbstractMenu{
public abstract void add(AbstractMenu menu);
public abstract AbstractMenu get(int index);
public abstract String getString();
}
class MileTeaNew extends AbstractMenu{
private String name;
private String desc;
private int price;
public MileTeaNew(String name, String desc, int i) {
super();
this.name = name;
this.desc = desc;
this.price = i;
}
@Override
public void add(AbstractMenu menu) {
/*未使用*/
}
@Override
public AbstractMenu get(int index) {
return null;
}
@Override
public String getString() {
return toString();
}
@Override
public String toString() {
return "MileTeaNew [name=" + name + ", desc=" + desc + ", price="
+ price + "]";
}
}
class JuiceNew extends AbstractMenu{
private String name;
private String desc;
private int price;
public JuiceNew(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
@Override
public void add(AbstractMenu menu) {
/*未使用*/
}
@Override
public AbstractMenu get(int index) {
return null;
}
@Override
public String getString() {
return toString();
}
@Override
public String toString() {
return "MileTeaNew [name=" + name + ", desc=" + desc + ", price="
+ price + "]";
}
}
class HandCakeNew extends AbstractMenu{
private String name;
private String desc;
private int price;
public HandCakeNew(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
@Override
public void add(AbstractMenu menu) {
/*未使用*/
}
@Override
public AbstractMenu get(int index) {
return null;
}
@Override
public String getString() {
return toString();
}
@Override
public String toString() {
return "MileTeaNew [name=" + name + ", desc=" + desc + ", price="
+ price + "]";
}
}
class FishBollNew extends AbstractMenu{
private String name;
private String desc;
private int price;
public FishBollNew(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
@Override
public void add(AbstractMenu menu) {
/*未使用*/
}
@Override
public AbstractMenu get(int index) {
return null;
}
@Override
public String getString() {
return toString();
}
@Override
public String toString() {
return "MileTeaNew [name=" + name + ", desc=" + desc + ", price="
+ price + "]";
}
}
class MenuNew extends AbstractMenu{
private String name;
private String desc;
private List<AbstractMenu> menus = new ArrayList<>();
public MenuNew(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
@Override
public void add(AbstractMenu menu) {
this.menus.add(menu);
}
@Override
public AbstractMenu get(int index) {
return this.menus.get(index);
}
@Override
public String getString() {
return toString();
}
@Override
public String toString() {
return "MenuNew [name=" + name + ", desc=" + desc + ", menus=" + menus
+ "]";
}
}
测试例程:
MenuNew menuNew = new MenuNew("大菜单","包含所有的子菜单");
MenuNew drinkMenuNew = new MenuNew("饮品菜单","都是喝的");
MenuNew eatMenuNew = new MenuNew("小吃菜单","都是吃的");
MileTeaNew milkTeaNew = new MileTeaNew("珍珠卖茶","珍珠+奶茶",6);
JuiceNew juiceNew = new JuiceNew("猕猴桃饮料","无添加剂");
HandCakeNew handCakeNew = new HandCakeNew("咖喱鱼蛋","微辣");
FishBollNew fishBollNew = new FishBollNew("培根手抓饼","正宗台湾风味");
drinkMenuNew.add(milkTeaNew);
drinkMenuNew.add(juiceNew);
eatMenuNew.add(handCakeNew);
eatMenuNew.add(fishBollNew);
menuNew.add(drinkMenuNew);
menuNew.add(eatMenuNew);
menuNew.getString();
使用了合并模式,如果此时我们要新增一个菜品,只需继承抽象构建类, 无需改动其他类,显得更加方便。
概念与总结
三个角色
上面也说了合并模式是用一种树状的结构来组合对象,三个名词 根节点,枝结点,叶子结点,类比上面那个菜单的图, 根节点是菜单,枝结点是饮料菜单和小吃菜单, 叶子结点是奶茶,果汁,手抓饼和鱼蛋!
Component:抽象组件,为组合中的对象声明接口,让客户端 可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的 功能提供缺省的实现,比如上面的AbstractMenu类。
Composite:容器组件,继承抽象组件,实现抽象组件中与 叶子组件相关的操作,比如上面的Menu类重写了get,set方法。此处重写的是toString方法也就是,抽象类中的getString方法
Leaf:叶子组件,定义和实现叶子对象的行为,不再包含其它 的子节点对象,比如上面的MilkTea,Juice,HandCakeFishBall。
UML图
使用情景
- 如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外 部来使用这个层次结构也简单;
- 如果你希望统一的使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能;
优缺点
优点:
让客户端更加简单,客户端不需要再操心面对的是组合对象还是叶节点对象,所以不需要写一大堆if语句来保证他们对正确的对象调用了正确 的方法。通常,他们只需要对整个结构调用一个方法并执行操作就可以了。
缺点:
容易增加新的组件也会带来一些问题,比如很难限制组合中的组件类型。 这在需要检测组件类型的时候,使得我们不能依靠编译期的类型约束来 完成,必须在运行期间动态检测。