参考:http://c.biancheng.net/view/1397.html
什么是访问者模式 访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
优点:
扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点:
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
结构与实现 访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类,其基本结构与实现方法如下。
访问者模式包含以下主要角色。
抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public class VisitorPattern { public static void main (String[] args) { ObjectStructure os = new ObjectStructure (); os.add(new ConcreteElementA ()); os.add(new ConcreteElementB ()); Visitor visitor = new ConcreteVisitorA (); os.accept(visitor); System.out.println("------------------------" ); visitor = new ConcreteVisitorB (); os.accept(visitor); } } interface Visitor { void visit (ConcreteElementA element) ; void visit (ConcreteElementB element) ; } class ConcreteVisitorA implements Visitor { public void visit (ConcreteElementA element) { System.out.println("具体访问者A访问-->" + element.operationA()); } public void visit (ConcreteElementB element) { System.out.println("具体访问者A访问-->" + element.operationB()); } } class ConcreteVisitorB implements Visitor { public void visit (ConcreteElementA element) { System.out.println("具体访问者B访问-->" + element.operationA()); } public void visit (ConcreteElementB element) { System.out.println("具体访问者B访问-->" + element.operationB()); } } interface Element { void accept (Visitor visitor) ; } class ConcreteElementA implements Element { public void accept (Visitor visitor) { visitor.visit(this ); } public String operationA () { return "具体元素A的操作。" ; } } class ConcreteElementB implements Element { public void accept (Visitor visitor) { visitor.visit(this ); } public String operationB () { return "具体元素B的操作。" ; } } class ObjectStructure { private List<Element> list = new ArrayList <Element>(); public void accept (Visitor visitor) { Iterator<Element> i = list.iterator(); while (i.hasNext()) { ((Element) i.next()).accept(visitor); } } public void add (Element element) { list.add(element); } public void remove (Element element) { list.remove(element); } }
运行结果:
1 2 3 4 5 具体访问者A访问-->具体元素A的操作。 具体访问者A访问-->具体元素B的操作。 ------------------------ 具体访问者B访问-->具体元素A的操作。 具体访问者B访问-->具体元素B的操作。
应用实例 【例1】利用“访问者(Visitor)模式”模拟艺术公司与造币公司的功能。
分析:艺术公司利用“铜”可以设计出铜像,利用“纸”可以画出图画;造币公司利用“铜”可以印出铜币,利用“纸”可以印出纸币(点此下载运行该程序后所要显示的图片 )。对“铜”和“纸”这两种元素,两个公司的处理方法不同,所以该实例用访问者模式来实现比较适合。
首先,定义一个公司(Company)接口,它是抽象访问者,提供了两个根据纸(Paper)或铜(Cuprum)这两种元素创建作品的方法;再定义艺术公司(ArtCompany)类和造币公司(Mint)类,它们是具体访问者,实现了父接口的方法。
然后,定义一个材料(Material)接口,它是抽象元素,提供了 accept(Company visitor)方法来接受访问者(Company)对象访问;再定义纸(Paper)类和铜(Cuprum)类,它们是具体元素类,实现了父接口中的方法。
最后,定义一个材料集(SetMaterial)类,它是对象结构角色,拥有保存所有元素的容器 List,并提供让访问者对象遍历容器中的所有元素的 accept(Company visitor)方法;客户类设计成窗体程序,它提供材料集(SetMaterial)对象供访问者(Company)对象访问,实现了 ItemListener 接口,处理用户的事件请求。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public class VisitorProducer { public static void main (String[] args) { new MaterialWin (); } } class MaterialWin extends JFrame implements ItemListener { private static final long serialVersionUID = 1L ; JPanel CenterJP; SetMaterial os; Company visitor1, visitor2; String[] select; MaterialWin() { super ("利用访问者模式设计艺术公司和造币公司" ); JRadioButton Art; JRadioButton mint; os = new SetMaterial (); os.add(new Cuprum ()); os.add(new Paper ()); visitor1 = new ArtCompany (); visitor2 = new Mint (); this .setBounds(10 , 10 , 750 , 350 ); this .setResizable(false ); CenterJP = new JPanel (); this .add("Center" , CenterJP); JPanel SouthJP = new JPanel (); JLabel yl = new JLabel ("原材料有:铜和纸,请选择生产公司:" ); Art = new JRadioButton ("艺术公司" , true ); mint = new JRadioButton ("造币公司" ); Art.addItemListener(this ); mint.addItemListener(this ); ButtonGroup group = new ButtonGroup (); group.add(Art); group.add(mint); SouthJP.add(yl); SouthJP.add(Art); SouthJP.add(mint); this .add("South" , SouthJP); select = (os.accept(visitor1)).split(" " ); showPicture(select[0 ], select[1 ]); } void showPicture (String Cuprum, String paper) { CenterJP.removeAll(); CenterJP.repaint(); String FileName1 = "src/visitor/Picture/" + Cuprum + ".jpg" ; String FileName2 = "src/visitor/Picture/" + paper + ".jpg" ; JLabel lb = new JLabel (new ImageIcon (FileName1), JLabel.CENTER); JLabel rb = new JLabel (new ImageIcon (FileName2), JLabel.CENTER); CenterJP.add(lb); CenterJP.add(rb); this .setVisible(true ); this .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } @Override public void itemStateChanged (ItemEvent arg0) { JRadioButton jc = (JRadioButton) arg0.getSource(); if (jc.isSelected()) { if (jc.getText() == "造币公司" ) { select = (os.accept(visitor2)).split(" " ); } else { select = (os.accept(visitor1)).split(" " ); } showPicture(select[0 ], select[1 ]); } } } interface Company { String create (Paper element) ; String create (Cuprum element) ; } class ArtCompany implements Company { public String create (Paper element) { return "讲学图" ; } public String create (Cuprum element) { return "朱熹铜像" ; } } class Mint implements Company { public String create (Paper element) { return "纸币" ; } public String create (Cuprum element) { return "铜币" ; } } interface Material { String accept (Company visitor) ; } class Paper implements Material { public String accept (Company visitor) { return (visitor.create(this )); } } class Cuprum implements Material { public String accept (Company visitor) { return (visitor.create(this )); } } class SetMaterial { private List<Material> list = new ArrayList <Material>(); public String accept (Company visitor) { Iterator<Material> i = list.iterator(); String tmp = "" ; while (i.hasNext()) { tmp += ((Material) i.next()).accept(visitor) + " " ; } return tmp; } public void add (Material element) { list.add(element); } public void remove (Material element) { list.remove(element); } }
运行结果:
【例2】这是我自己用访问者模式模拟出不同客户购买电脑所得价格的功能。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface Visitor { void visitCPU (CPU cpu ,int count) ; void visitComputerCase (ComputerCase computerCaseint ,int count) ; void visitCpuHeatSink (CpuHeatSink cpuHeatSinkint ,int count) ; void visitFan (Fan Fanint ,int count) ; void visitGraphicsCard (GraphicsCard GraphicsCardint ,int count) ; void visitMainboard (Mainboard mainboardint ,int count) ; void visitMemoryBank (MemoryBank memoryBankint ,int count) ; void visitPowerSupply (PowerSupply powerSupplyint ,int count) ; void visitSSD (SSD ssdint ,int count) ; double getTotalPrice () ; }
1 2 3 4 5 public interface ComputerPart { void accept (Visitor visitor) ; double getPrice () ; }
1 2 3 4 5 6 7 8 9 10 11 12 public class ComputerCase implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitComputerCase(this ,1 ); } @Override public double getPrice () { return 800 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class CPU implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitCPU(this ,1 ); } @Override public double getPrice () { return 3000 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class CpuHeatSink implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitCpuHeatSink(this ,1 ); } @Override public double getPrice () { return 500 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class Fan implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitFan(this ,7 ); } @Override public double getPrice () { return 30 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class GraphicsCard implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitGraphicsCard(this ,1 ); } @Override public double getPrice () { return 4800 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class Mainboard implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitMainboard(this ,1 ); } @Override public double getPrice () { return 2000 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class MemoryBank implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitMemoryBank(this ,2 ); } @Override public double getPrice () { return 500 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class PowerSupply implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitPowerSupply(this ,1 ); } @Override public double getPrice () { return 800 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class SSD implements ComputerPart { @Override public void accept (Visitor visitor) { visitor.visitSSD(this ,2 ); } @Override public double getPrice () { return 800 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 public class StudentVisitor implements Visitor { private double discounts; private double totalPrice; public StudentVisitor (double discounts) { this .discounts = discounts; } @Override public void visitCPU (CPU cpu,int count) { double v = cpu.getPrice() * discounts; totalPrice+=v*count; System.out.print("cpu:原价:" +cpu.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitComputerCase (ComputerCase computerCase,int count) { double v = computerCase.getPrice() * discounts; totalPrice+=v*count; System.out.print("机箱:原价:" +computerCase.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitCpuHeatSink (CpuHeatSink cpuHeatSink,int count) { double v = cpuHeatSink.getPrice() * discounts; totalPrice+=v*count; System.out.print("CPU散热器:原价:" +cpuHeatSink.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitFan (Fan fan,int count) { double v = fan.getPrice() * discounts; totalPrice+=v*count; System.out.print("风扇:原价:" +fan.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitGraphicsCard (GraphicsCard graphicsCard,int count) { double v = graphicsCard.getPrice() * discounts; totalPrice+=v*count; System.out.print("显卡:原价:" +graphicsCard.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitMainboard (Mainboard mainboard,int count) { double v = mainboard.getPrice() * discounts; totalPrice+=v*count; System.out.print("主板:原价:" +mainboard.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitMemoryBank (MemoryBank memoryBank,int count) { double v = memoryBank.getPrice() * discounts; totalPrice+=v*count; System.out.print("内存条:原价:" +memoryBank.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitPowerSupply (PowerSupply powerSupply,int count) { double v = powerSupply.getPrice() * discounts; totalPrice+=v*count; System.out.print("电源:原价:" +powerSupply.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitSSD (SSD ssd,int count) { double v = ssd.getPrice() * discounts; totalPrice+=v*count; System.out.print("固态硬盘:原价:" +ssd.getPrice()); System.out.println(" 优惠价:" +v); } @Override public double getTotalPrice () { return this .totalPrice; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 public class WorkVisitor implements Visitor { private double discounts; private double totalPrice; public WorkVisitor (double discounts) { this .discounts = discounts; } @Override public void visitCPU (CPU cpu,int count) { double v = cpu.getPrice() * discounts; totalPrice+=v*count; System.out.print("cpu:原价:" +cpu.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitComputerCase (ComputerCase computerCase,int count) { double v = computerCase.getPrice() * discounts; totalPrice+=v*count; System.out.print("机箱:原价:" +computerCase.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitCpuHeatSink (CpuHeatSink cpuHeatSink,int count) { double v = cpuHeatSink.getPrice() * discounts; totalPrice+=v*count; System.out.print("CPU散热器:原价:" +cpuHeatSink.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitFan (Fan fan,int count) { double v = fan.getPrice() * discounts; totalPrice+=v*count; System.out.print("风扇:原价:" +fan.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitGraphicsCard (GraphicsCard graphicsCard,int count) { double v = graphicsCard.getPrice() * discounts; totalPrice+=v*count; System.out.print("显卡:原价:" +graphicsCard.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitMainboard (Mainboard mainboard,int count) { double v = mainboard.getPrice() * discounts; totalPrice+=v*count; System.out.print("主板:原价:" +mainboard.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitMemoryBank (MemoryBank memoryBank,int count) { double v = memoryBank.getPrice() * discounts; totalPrice+=v*count; System.out.print("内存条:原价:" +memoryBank.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitPowerSupply (PowerSupply powerSupply,int count) { double v = powerSupply.getPrice() * discounts; totalPrice+=v*count; System.out.print("电源:原价:" +powerSupply.getPrice()); System.out.println(" 优惠价:" +v); } @Override public void visitSSD (SSD ssd,int count) { double v = ssd.getPrice() * discounts; totalPrice+=v*count; System.out.print("固态硬盘:原价:" +ssd.getPrice()); System.out.println(" 优惠价:" +v); } @Override public double getTotalPrice () { return this .totalPrice; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ObjectStructure { private List<ComputerPart> list = new ArrayList <>(); public void accept (Visitor visitor) { Iterator<ComputerPart> i = list.iterator(); while (i.hasNext()) { (i.next()).accept(visitor); } } public void add (ComputerPart part) { list.add(part); } public void remove (ComputerPart part) { list.remove(part); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Computer { public static void main (String[] args) { ObjectStructure os = new ObjectStructure (); os.add(new CPU ()); os.add(new CpuHeatSink ()); os.add(new Mainboard ()); os.add(new MemoryBank ()); os.add(new SSD ()); os.add(new GraphicsCard ()); os.add(new PowerSupply ()); os.add(new Fan ()); os.add(new ComputerCase ()); System.out.println("************学生优惠************" ); Visitor studentVisitor = new StudentVisitor (0.75 ); os.accept(studentVisitor); System.out.println("总价:" +studentVisitor.getTotalPrice()); System.out.println(); System.out.println("************职工优惠************" ); Visitor workVisitor = new WorkVisitor (0.9 ); os.accept(workVisitor); System.out.println("总价:" +workVisitor.getTotalPrice()); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ************学生优惠************ cpu:原价:3000.0 优惠价:2250.0 CPU散热器:原价:500.0 优惠价:375.0 主板:原价:2000.0 优惠价:1500.0 内存条:原价:500.0 优惠价:375.0 固态硬盘:原价:800.0 优惠价:600.0 显卡:原价:4800.0 优惠价:3600.0 电源:原价:800.0 优惠价:600.0 风扇:原价:30.0 优惠价:22.5 机箱:原价:800.0 优惠价:600.0 总价:11032.5 ************职工优惠************ cpu:原价:3000.0 优惠价:2700.0 CPU散热器:原价:500.0 优惠价:450.0 主板:原价:2000.0 优惠价:1800.0 内存条:原价:500.0 优惠价:450.0 固态硬盘:原价:800.0 优惠价:720.0 显卡:原价:4800.0 优惠价:4320.0 电源:原价:800.0 优惠价:720.0 风扇:原价:30.0 优惠价:27.0 机箱:原价:800.0 优惠价:720.0 总价:13239.0
应用场景 当系统中存在类型数量稳定(固定)的一类数据结构时,可以使用访问者模式方便地实现对该类型所有数据结构的不同操作,而又不会对数据产生任何副作用(脏数据)。
简而言之,就是当对集合中的不同类型数据(类型数量稳定)进行多种操作时,使用访问者模式。
通常在以下情况可以考虑使用访问者(Visitor)模式。
对象结构相对稳定,但其操作算法经常变化的程序。
对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
扩展 访问者(Visitor)模式是使用频率较高的一种设计模式 ,它常常同以下两种设计模式联用。
(1)与“迭代器模式 ”联用。因为访问者模式中的“对象结构”是一个包含元素角色的容器,当访问者遍历容器中的所有元素时,常常要用迭代器。如【例1】中的对象结构是用 List 实现的,它通过 List 对象的 Iterator() 方法获取迭代器。如果对象结构中的聚合类没有提供迭代器,也可以用迭代器模式自定义一个。
(2)访问者(Visitor)模式同“组合模式 ”联用。因为访问者(Visitor)模式中的“元素对象”可能是叶子对象或者是容器对象,如果元素对象包含容器对象,就必须用到组合模式 。