想要为一个对象增强新的行为,且不封装具体的实现,那么就用访问者模式
一般我们都是使用访问者模式增添遍历行为,这是一个误区。
模型
- Element 表示要增强的行为,即accept方法
- elementA 代表原有对象
- visitor表示抽象接口
- visitorA表示具体的行为实现
改进之前的例子
组合模式中的列子:https://blog.csdn.net/dengjili/article/details/79705777
这是一家餐厅的菜单结构,现在我们需要将其遍历输出
原模型设计
更改模型设计
基类代码
package headfirst.hd.visitor.eg;
public interface Element {
void accept(Visitor visitor);
}
package headfirst.hd.visitor.eg;
public abstract class Component implements Element{
// 集合操作-增
public void add(Component menuComponent) {
throw new UnsupportedOperationException();
}
// 集合操作-删
public void remove(Component menuComponent) {
throw new UnsupportedOperationException();
}
// 集合操作-查
public Component getChild(int i) {
throw new UnsupportedOperationException();
}
public String getName() {
throw new UnsupportedOperationException();
}
public float getPrice() {
throw new UnsupportedOperationException();
}
public void print(int offset) {
throw new UnsupportedOperationException();
}
}
菜单项
package headfirst.hd.visitor.eg;
//菜单项
public class Item extends Component {
private String name;
private float price;
public Item(String name, float price) {
this.name = name;
this.price = price;
}
@Override
public String getName() {
return name;
}
@Override
public float getPrice() {
return price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
组合菜单
package headfirst.hd.visitor.eg;
import java.util.ArrayList;
import java.util.List;
public class Menu extends Component {
List<Component> menuComponents = new ArrayList<Component>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
@Override
public void add(Component menuComponent) {
menuComponents.add(menuComponent);
}
@Override
public void remove(Component menuComponent) {
menuComponents.remove(menuComponent);
}
@Override
public Component getChild(int i) {
return menuComponents.get(i);
}
@Override
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<Component> getMenuComponents() {
return menuComponents;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
访问者类
package headfirst.hd.visitor.eg;
public interface Visitor {
void visit(Element e);
}
package headfirst.hd.visitor.eg;
public class ItemVisitor implements Visitor {
@Override
public void visit(Element e) {
if (e instanceof Item) {
Item item = (Item) e;
System.out.println("[商品名=" + item.getName() + ", 价格=" + item.getPrice() + "]");
}
}
}
package headfirst.hd.visitor.eg;
import java.util.List;
public class MenuVisitor implements Visitor {
@Override
public void visit(Element e) {
if (Menu.class.isAssignableFrom(e.getClass())) {
Menu item = (Menu) e;
System.out.println("{菜单名=" + item.getName() + ", 描述=" + item.getDescription() + "}");
// 遍历子节点
List<Component> menuComponents = item.getMenuComponents();
for (Component component : menuComponents) {
if (Menu.class.isAssignableFrom(component.getClass())) {
component.accept(this);
} else {
component.accept(new ItemVisitor());
}
}
}
}
}
客户端类
package headfirst.hd.visitor.eg;
public class Waitress {
Component menu;
public Waitress(Component menu) {
this.menu = menu;
}
// 遍历方法
public void printMenu(Visitor visitor) {
menu.accept(visitor);
}
}
测试类
package headfirst.hd.visitor.eg;
public class DriveTest {
public static void main(String[] args) {
Component menu = simpleFactory();
Waitress waitress = new Waitress(menu);
//打印菜单
waitress.printMenu(new MenuVisitor());
}
//创建一份菜单
private static Component simpleFactory() {
//菜单
Component menu = new Menu("菜单", "xxx饭店菜单");
//菜单项
Component menuA = new Menu("饭类", "类别:主食类");
Component menuB = new Menu("水类", "类别:补充水分");
Component menuC = new Menu("小吃类", "类别:零食");
Component menuD = new Menu("汤类", "类别:水类的子类");
menu.add(menuA);
menu.add(menuB);
menu.add(menuC);
//菜单项中具体类别
Component itemA1 = new Item("牛肉", 15f);
Component itemA2 = new Item("鸡肉", 13f);
Component itemA3 = new Item("鸭肉", 14f);
menuA.add(itemA1);
menuA.add(itemA2);
menuA.add(itemA3);
Component itemB1 = new Item("红茶", 4f);
Component itemB2 = new Item("奶茶", 6f);
menuB.add(itemB1);
menuB.add(itemB2);
menuB.add(menuD); //菜单项的包含
Component itemC1 = new Item("烤翅", 8f);
Component itemC2 = new Item("尾虾", 8f);
Component itemC3 = new Item("鱿鱼", 5f);
menuC.add(itemC1);
menuC.add(itemC2);
menuC.add(itemC3);
Component itemD1 = new Item("菜汤", 6f);
Component itemD2 = new Item("酸汤", 6f);
Component itemD3 = new Item("其他汤", 5f);
menuD.add(itemD1);
menuD.add(itemD2);
menuD.add(itemD3);
return menu;
}
}
测试结果
{菜单名=菜单, 描述=xxx饭店菜单}
{菜单名=饭类, 描述=类别:主食类}
[商品名=牛肉, 价格=15.0]
[商品名=鸡肉, 价格=13.0]
[商品名=鸭肉, 价格=14.0]
{菜单名=水类, 描述=类别:补充水分}
[商品名=红茶, 价格=4.0]
[商品名=奶茶, 价格=6.0]
{菜单名=汤类, 描述=类别:水类的子类}
[商品名=菜汤, 价格=6.0]
[商品名=酸汤, 价格=6.0]
[商品名=其他汤, 价格=5.0]
{菜单名=小吃类, 描述=类别:零食}
[商品名=烤翅, 价格=8.0]
[商品名=尾虾, 价格=8.0]
[商品名=鱿鱼, 价格=5.0]
结论
访问者模式增添行为,但是不提供具体的实现,访问者模式=遍历,错误!!!
jdk使用访问者模式
Java8 新特性之集合: forEach(Consumer<? super T> action)