/*
菜单和菜单项
菜单
|--陕菜
|--胡辣汤
|--肉丸胡辣汤
|--河南胡辣汤
|--羊肉泡馍
|--优质
|--普通
|--双份优质
|--三秦套餐
|--川菜
|--火锅
|--辣椒就朝天椒
|--伤心凉粉
|--粤菜
|--鱼丸
|--虾丸
|--牛丸
*/
第一阶段:
package composite.a;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
菜单和菜单项
菜单
|--陕菜
|--胡辣汤
|--肉丸胡辣汤
|--河南胡辣汤
|--羊肉泡馍
|--优质
|--普通
|--双份优质
|--三秦套餐
|--川菜
|--火锅
|--辣椒就朝天椒
|--伤心凉粉
|--粤菜
|--鱼丸
|--虾丸
|--牛丸
*/
class Menu{
private String name;
private String description;
private List<MenuItem> list = new ArrayList<>();
public Menu(String name, String description) {
super();
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void add(MenuItem mi) {
list.add(mi);
}
public void print(String prefix) {
System.out.println(prefix +"<<"+name+">>"+description);
Iterator <MenuItem> it = list.iterator();
while(it.hasNext()) {
MenuItem mi = it.next();
mi.print("\t"+prefix);
}
}
}
class MenuItem{
private String name;
private String description;
public MenuItem(String name, String description) {
super();
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void print(String prefix) {
System.out.println(prefix +name + ":"+description);
}
}
public class AppTest {
public static void main(String[] args) {
Menu menu = new Menu("陕菜","老陕爱吃的菜");
MenuItem mi = new MenuItem("鱼香肉丝","没有鱼 ");
Menu menu2 = new Menu("胡辣汤","可以细分多种不同的胡辣汤");
MenuItem mi2 = new MenuItem("a","aaa");
MenuItem mi3 = new MenuItem("b","bbb");
menu.add(mi);
menu2.add(mi2);
menu2.add(mi3);
// Can't compile
//因为现在的菜单只能添加菜单项。不能添加其他菜单
//也就无法制作出多级菜单,也就是不能做出菜单的嵌套。
//menu.add(menu2);
menu.print("");
}
}
第二阶段:
随手小记: 递归调用完成之后,回到其原来调用的地方
上述问题是:
1.菜单只能添加菜单项,不能添加菜单
2.不能出现嵌套的菜单形式
重构代码如下,要达到的目的:
1.菜单可以添加菜单和菜单项
2.菜单项不能再添加菜单项
就可以出现嵌套的菜单形式。
如下图所示:
package composite.b;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//菜单组件,用于提升菜单和菜单项的共性:
abstract class MenuComponent{
private String name;
private String description;
public MenuComponent(String name, String description) {
super();
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public abstract void print(String prefix);
}
class Menu extends MenuComponent{
private List<MenuComponent> list = new ArrayList<>();
public Menu(String name, String description) {
super(name,description);
}
//此时,add方法,就可以同时添加Menu和MenuItem了。
public void add(MenuComponent mi) {
list.add(mi);
}
public void print(String prefix) {
System.out.println(prefix +"<<"+getName()+">>"+getDescription());
Iterator <MenuComponent> it = list.iterator();
while(it.hasNext()) {
MenuComponent mi = it.next();
mi.print("\t"+prefix);
}
}
}
class MenuItem extends MenuComponent{
public MenuItem(String name, String description) {
super(name,description);
}
public void print(String prefix) {
System.out.println(prefix +getName() + ":"+getDescription());
}
}
public class AppTest {
public static void main(String[] args) {
Menu menu = new Menu("蜗牛餐厅菜单","欢迎光临");
Menu menu1 = new Menu("陕菜","xxxxxxx");
Menu menu2 = new Menu("川菜","yyyyyy");
Menu menu3 = new Menu("鲁菜","zzzzzz");
MenuItem mi1 = new MenuItem("胡辣汤1" , "aaaaa");
MenuItem mi2 = new MenuItem("胡辣汤2" , "aaaaa");
MenuItem mi3 = new MenuItem("胡辣汤3" , "aaaaa");
MenuItem mi4 = new MenuItem("剁椒鱼头1" , "aaaaa");
MenuItem mi5 = new MenuItem("剁椒鱼头2" , "aaaaa");
MenuItem mi6 = new MenuItem("剁椒鱼头3" , "aaaaa");
MenuItem mi7 = new MenuItem("热干面1" , "aaaaa");
MenuItem mi8 = new MenuItem("热干面2" , "aaaaa");
MenuItem mi9 = new MenuItem("热干面3" , "aaaaa");
menu.add(menu1);
menu.add(menu2);
menu.add(menu3);
menu1.add(mi1);
menu1.add(mi2);
menu1.add(mi3);
menu2.add(mi4);
menu2.add(mi5);
menu2.add(mi6);
menu3.add(mi7);
menu3.add(mi8);
menu3.add(mi9);
menu.print("");
}
}
/*
*
*/
现在,蜗牛餐厅,改变了业务,因为有的客户端吃素,不吃肉。需要,有添加一个额外的功能,菜单中,只打印素食。
第三阶段:为了解决上面的问题:
1.客户需要素菜菜单
2.客户需要平价菜单
3.客户需要土豪菜单
4.客户需要辣菜菜单
重构代码如下:
package composite.c;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//菜单组件,用于提升菜单和菜单项的共性:
abstract class MenuComponent{
private String name;
private String description;
public MenuComponent(String name, String description) {
super();
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public abstract void print(String prefix);
}
class Menu extends MenuComponent{
private List<MenuComponent> list = new ArrayList<>();
public Menu(String name, String description) {
super(name,description);
}
//此时,add方法,就可以同时添加Menu和MenuItem了。
public void add(MenuComponent mi) {
list.add(mi);
}
public void print(String prefix) {
System.out.println(prefix +"<<"+getName()+">>"+getDescription());
Iterator <MenuComponent> it = list.iterator();
while(it.hasNext()) {
MenuComponent mi = it.next();
mi.print("\t"+prefix);
}
}
public List<MenuComponent> getList() {
return list;
}
}
class MenuItem extends MenuComponent{
private boolean vegetarian;
private double price;
public MenuItem(String name, String description,boolean vegetarian,double price) {
super(name,description);
this.vegetarian = vegetarian;
this.price = price;
}
public void print(String prefix) {
String str = vegetarian? "(素食)":"";
System.out.println(prefix +getName() + str+":"+getDescription());
}
public boolean isVegetarian() {
return vegetarian;
}
public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}
}
public class Test {
//========================================
public static void printV(Menu menu) {
Iterator<MenuComponent> it = menu.getList().iterator();
while(it.hasNext()) {
MenuComponent mc = it.next();
if(mc instanceof MenuItem) {
if(((MenuItem) mc).isVegetarian()) {
mc.print("");
}
}
if(mc instanceof Menu) {
printV((Menu)mc);
}
}
}
public static void main(String[] args) {
Menu menu = new Menu("蜗牛餐厅菜单","欢迎光临");
Menu menu1 = new Menu("陕菜","xxxxxxx");
Menu menu2 = new Menu("川菜","yyyyyy");
Menu menu3 = new Menu("鲁菜","zzzzzz");
MenuItem mi1 = new MenuItem("胡辣汤1" , "aaaaa",false,6);
MenuItem mi2 = new MenuItem("凉皮" , "aaaaa",true,6);
MenuItem mi3 = new MenuItem("胡辣汤3" , "aaaaa",false,6);
MenuItem mi4 = new MenuItem("剁椒鱼头1" , "aaaaa",false,12);
MenuItem mi5 = new MenuItem("干煸豆角" , "aaaaa",true,12);
MenuItem mi6 = new MenuItem("剁椒鱼头3" , "aaaaa",false,12);
MenuItem mi7 = new MenuItem("牛肉拉面" , "aaaaa",false,9);
MenuItem mi8 = new MenuItem("老干妈土豆丝" , "aaaaa",true,9);
MenuItem mi9 = new MenuItem("热干面3" , "aaaaa",true,9);
menu.add(menu1);
menu.add(menu2);
menu.add(menu3);
menu1.add(mi1);
menu1.add(mi2);
menu1.add(mi3);
menu2.add(mi4);
menu2.add(mi5);
menu2.add(mi6);
menu3.add(mi7);
menu3.add(mi8);
menu3.add(mi9);
// menu.print("");
printV(menu);
}
}
上述代码中出现的递归调用:
现在,很灵活,但是,客户端必须知道底层的数据结构。(迭代器模式)
另外一个缺点是,客户端必须知道MenuItem和Menu的存在。客户端应该只知道MenuCompunent的存在就足够了。(组合模式来解决)
第四阶段:为了解决c包中的问题,重构代码如下:
组合模式:将对象组合成树状结构,以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚“部分/整体”还有“单个对象”与“组合对象”的含义。
在这里,单个对象指的是菜单项;组合对象指的是菜单。
package composite.d;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//为了保证菜单和菜单项具有相同的接口,所以在他们的父类
//这里,把菜单需要的方法和菜单项需要的方法统统定义出来。
abstract class MenuComponent{
private String name;
private String description;
public MenuComponent(String name, String description) {
super();
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public abstract void print(String prefix);
//属于菜单的方法 : add remove getChild
//这些方法对于菜单而言,是有意义的,但是对于菜单项而言没有意义。
//那么为什么还要定义在这个父类中呢?
//为的就是:组合模式中,使得用户对单个对象和组合对象的使用具有一致性。
public void add(MenuComponent mc) {
//抛出不支持的操作异常
throw new UnsupportedOperationException();
}
public void remove(MenuComponent mc) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
//属于菜单项的方法:getPrice isVegetarian()
//同样,对于菜单项而言,为了和组合对象的使用具有一致性。
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public List<MenuComponent> getList(){
throw new UnsupportedOperationException();
};
}
class Menu extends MenuComponent{
private List<MenuComponent> list = new ArrayList<>();
public Menu(String name, String description) {
super(name,description);
}
//此时,add方法,就可以同时添加Menu和MenuItem了。
public void add(MenuComponent mc) {
list.add(mc);
}
public void remove(MenuComponent mc) {
list.remove(mc);
}
public MenuComponent getChild(int i) {
return list.get(i);
}
public void print(String prefix) {
System.out.println(prefix +"<<"+getName()+">>"+getDescription());
Iterator <MenuComponent> it = list.iterator();
while(it.hasNext()) {
MenuComponent mi = it.next();
mi.print("\t"+prefix);
}
}
public List<MenuComponent> getList() {
return list;
}
}
class MenuItem extends MenuComponent{
private boolean vegetarian;
private double price;
public MenuItem(String name, String description,boolean vegetarian,double price) {
super(name,description);
this.vegetarian = vegetarian;
this.price = price;
}
public void print(String prefix) {
String str = vegetarian? "(素食)":"";
System.out.println(prefix +getName() + str+":"+getDescription());
}
public boolean isVegetarian() {
return vegetarian;
}
public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
public class Test {
//========================================
public static void printV(MenuComponent me) {
Iterator<MenuComponent> it = me.getList().iterator();
while(it.hasNext()) {
MenuComponent mc = it.next();
try {
if(mc.isVegetarian()) {
mc.print("");
}
} catch (Exception e) {
printV(mc);
}
}
}
public static void main(String[] args) {
Menu menu = new Menu("蜗牛餐厅菜单","欢迎光临");
Menu menu1 = new Menu("陕菜","xxxxxxx");
Menu menu2 = new Menu("川菜","yyyyyy");
Menu menu3 = new Menu("鲁菜","zzzzzz");
MenuItem mi1 = new MenuItem("胡辣汤1" , "aaaaa",false,6);
MenuItem mi2 = new MenuItem("凉皮" , "aaaaa",true,6);
MenuItem mi3 = new MenuItem("胡辣汤3" , "aaaaa",false,6);
MenuItem mi4 = new MenuItem("剁椒鱼头1" , "aaaaa",false,12);
MenuItem mi5 = new MenuItem("干煸豆角" , "aaaaa",true,12);
MenuItem mi6 = new MenuItem("剁椒鱼头3" , "aaaaa",false,12);
MenuItem mi7 = new MenuItem("牛肉拉面" , "aaaaa",false,9);
MenuItem mi8 = new MenuItem("老干妈土豆丝" , "aaaaa",true,9);
MenuItem mi9 = new MenuItem("热干面3" , "aaaaa",true,9);
menu.add(menu1);
menu.add(menu2);
menu.add(menu3);
menu1.add(mi1);
menu1.add(mi2);
menu1.add(mi3);
menu2.add(mi4);
menu2.add(mi5);
menu2.add(mi6);
menu3.add(mi7);
menu3.add(mi8);
menu3.add(mi9);
// menu.print("");
printV(menu);
}
}
第五阶段:此时客户端只依赖于MenuComponent,而不再知道Menu和MenuItem的存在了,这样符合了最少知道原则。
此时,仍然需要客户端自己写递归代码,这样对客户端不友好,最理想的情况是,hasNext , next
为了解决d包中的问题:
客户端需要自己写递归代码,重构代码如下:让客户端只需要调用hasNext 和next即可。
package composite.e;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
//为了保证菜单和菜单项具有相同的接口,所以在他们的父类
//这里,把菜单需要的方法和菜单项需要的方法统统定义出来。
abstract class MenuComponent{
private String name;
private String description;
public MenuComponent(String name, String description) {
super();
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public abstract void print(String prefix);
//属于菜单的方法 : add remove getChild
//这些方法对于菜单而言,是有意义的,但是对于菜单项而言没有意义。
//那么为什么还要定义在这个父类中呢?
//为的就是:组合模式中,使得用户对单个对象和组合对象的使用具有一致性。
public void add(MenuComponent mc) {
//抛出不支持的操作异常
throw new UnsupportedOperationException();
}
public void remove(MenuComponent mc) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
//属于菜单项的方法:getPrice isVegetarian()
//同样,对于菜单项而言,为了和组合对象的使用具有一致性。
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public List<MenuComponent> getList(){
throw new UnsupportedOperationException();
};
}
class Menu extends MenuComponent{
private List<MenuComponent> list = new ArrayList<>();
public Menu(String name, String description) {
super(name,description);
}
//此时,add方法,就可以同时添加Menu和MenuItem了。
public void add(MenuComponent mc) {
list.add(mc);
}
public void remove(MenuComponent mc) {
list.remove(mc);
}
public MenuComponent getChild(int i) {
return list.get(i);
}
public void print(String prefix) {
System.out.println(prefix +"<<"+getName()+">>"+getDescription());
Iterator <MenuComponent> it = list.iterator();
while(it.hasNext()) {
MenuComponent mi = it.next();
mi.print("\t"+prefix);
}
}
public List<MenuComponent> getList() {
return list;
}
public CompositeIterator iterator() {
return new CompositeIterator(list.iterator());
}
}
class MenuItem extends MenuComponent{
private boolean vegetarian;
private double price;
public MenuItem(String name, String description,boolean vegetarian,double price) {
super(name,description);
this.vegetarian = vegetarian;
this.price = price;
}
public void print(String prefix) {
String str = vegetarian? "(素食)":"";
System.out.println(prefix +getName() + str+":"+getDescription());
}
public boolean isVegetarian() {
return vegetarian;
}
public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
class CompositeIterator implements Iterator<MenuComponent>{
private Stack<Iterator<MenuComponent>> s = new Stack();
public CompositeIterator (Iterator<MenuComponent> it) {
s.push(it);
}
public boolean hasNext() {
if(s.isEmpty()) {
return false;
}else {
Iterator<MenuComponent> it = s.peek();
if(!it.hasNext()) {
s.pop();
return hasNext();
}else {
return true;
}
}
}
@Override
public MenuComponent next() {
Iterator<MenuComponent> it = s.peek();
MenuComponent mc = it.next();
if(mc instanceof Menu) {
s.push(((Menu)mc).getList().iterator());
}
return mc;
}
}
public class Test {
public static void main(String[] args) {
Menu menu = new Menu("蜗牛餐厅菜单","欢迎光临");
Menu menu1 = new Menu("陕菜","xxxxxxx");
Menu menu2 = new Menu("川菜","yyyyyy");
Menu menu3 = new Menu("鲁菜","zzzzzz");
MenuItem mi1 = new MenuItem("胡辣汤1" , "aaaaa",false,6);
MenuItem mi2 = new MenuItem("凉皮" , "aaaaa",true,6);
MenuItem mi3 = new MenuItem("胡辣汤3" , "aaaaa",false,6);
MenuItem mi4 = new MenuItem("剁椒鱼头1" , "aaaaa",false,12);
MenuItem mi5 = new MenuItem("干煸豆角" , "aaaaa",true,12);
MenuItem mi6 = new MenuItem("剁椒鱼头3" , "aaaaa",false,12);
MenuItem mi7 = new MenuItem("牛肉拉面" , "aaaaa",false,9);
MenuItem mi8 = new MenuItem("老干妈土豆丝" , "aaaaa",true,9);
MenuItem mi9 = new MenuItem("热干面3" , "aaaaa",true,9);
menu.add(menu1);
menu.add(menu2);
menu.add(menu3);
menu1.add(mi1);
menu1.add(mi2);
menu1.add(mi3);
menu2.add(mi4);
menu2.add(mi5);
menu2.add(mi6);
menu3.add(mi7);
menu3.add(mi8);
menu3.add(mi9);
CompositeIterator iterator = menu.iterator();
while(iterator.hasNext()) {
MenuComponent next = iterator.next();
try {
if(next.isVegetarian()) {
System.out.println(next.getName()+" "+next.getDescription());
}
} catch (Exception e) {
//吞异常
}
}
}
}
其中,表示迭代器模式的代码如下:
class CompositeIterator implements Iterator<MenuComponent>{
private Stack<Iterator<MenuComponent>> s = new Stack();
public CompositeIterator (Iterator<MenuComponent> it) {
s.push(it);
}
public boolean hasNext() {
if(s.isEmpty()) {
return false;
}else {
Iterator<MenuComponent> it = s.peek();
if(!it.hasNext()) {
s.pop();
return hasNext();
}else {
return true;
}
}
}
@Override
public MenuComponent next() {
Iterator<MenuComponent> it = s.peek();
MenuComponent mc = it.next();
if(mc instanceof Menu) {
s.push(((Menu)mc).getList().iterator());
}
return mc;
}
}
迭代器模式的解释:具体见设计模式-mr.gaoP75/P76
如图所示为迭代器设计模式的示意图