今天在使用dom4j进行xml解析的时候使用到了访问者模式。
它的使用是这样的,由于dom4j的Node接口中定义了一个accept(Visitor visitor)方法,而Node虚类是dom4j中所有的节点的父类,就相当于Object类一样,但是我们对于xml中的节点的访问应该是不一样的,对于属性,我们应当访问属性名和属性值,对于元素我们应当访问元素值和元素名,对于处理指令我们又要使用不一样的方法访问,他们的调用方法不尽相同。
为了方便,我们定义了一个访问者接口:Visitor接口;
Visitor接口提供了这样一些方法:
void visit(Document document); void visit(DocumentType documentType); void visit(Element node); void visit(Attribute node); void visit(CDATA node); void visit(Comment node); ………………
可以看到它基本上把所有的元素都考虑到了,就是说它为每一种元素都提供了一种访问方式。
具体实现的算法却是在具体的类中完成的。
下面我们来具体说说什么叫做访问者模式
假设有这样一种情况:现在有个外国军官要来访问中国,他想要看中国的武器,当然了中国为了澄清"中国威胁论",当然要给他看了,有两种武器得给他看啊,第一个是核弹头。第二个就当是J20吧。那么怎么给他看呢?他们都属于武器范畴,但是不一样的东西啊。并且不能把核心机密给泄密了呀?那怎么办呢?访问者模式可以帮得上忙了。
给每个武器都添加一个可以接受访问的方法accept(这个当然是要放在超类中的),然后具体如何访问则要在访问者中定义了。好吧!请看代码
package com.gengu.访问者模式; import java.util.Date; /** * 武器类 * 描述武器的相关信息 * */ public abstract class Weapon { //型号 private String name; //用途 private String UsdFor; //出厂日期 private Date date; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUsdFor() { return UsdFor; } public void setUsdFor(String usdFor) { UsdFor = usdFor; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public abstract void accept(Visitor visitor); }
两种武器
package com.gengu.访问者模式; /** * J20战斗机 * */ public class J20 extends Weapon{ //性能 private String performance; public String getPerformance() { return performance; } public void setPerformance(String performance) { this.performance = performance; } @Override public void accept(Visitor visitor) { visitor.visit(this); } } package com.gengu.访问者模式; /** * 这里是核弹 * */ public class Nbomb extends Weapon{ //核当量 private String equivalent; public String getEquivalent() { return equivalent; } public void setEquivalent(String equivalent) { this.equivalent = equivalent; } @Override public void accept(Visitor visitor) { visitor.visit(this); } }
然后就是访问者了
package com.gengu.访问者模式; /** * 访问接口 * */ public interface Visitor { /**我可以访问弹头*/ public void visit(Nbomb nbomb); /**还可以访问J-20*/ public void visit(J20 j20); }
访问者类的具体接口
package com.gengu.访问者模式; public class VisitorImpl implements Visitor{ public void visit(){} @Override public void visit(Nbomb nbomb) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("武器的名字:"+nbomb.getName()+"\n"); stringBuffer.append("出厂日期:"+nbomb.getDate()+"\n"); stringBuffer.append("战略用途:"+nbomb.getUsdFor()+"\n"); stringBuffer.append("武器的核当量:"+nbomb.getEquivalent()); System.out.println(stringBuffer.toString()); } @Override public void visit(J20 j20) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("武器的名字:"+j20.getName()+"\n"); stringBuffer.append("出厂日期:"+j20.getDate()+"\n"); stringBuffer.append("战略用途:"+j20.getUsdFor()+"\n"); stringBuffer.append("武器的核当量:"+j20.getPerformance()); System.out.println(stringBuffer.toString()); } }
下面是场景类
package com.gengu.访问者模式; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Client { public static void main(String[] args) { for(Weapon weapon:makeWeapon()){ weapon.accept(new VisitorImpl()); System.out.println("================"); } } public static List<Weapon> makeWeapon(){ List<Weapon> weapons = new ArrayList<Weapon>(); Nbomb nbomb = new Nbomb(); nbomb.setDate(new Date()); nbomb.setEquivalent("4000W"); nbomb.setName("核弹头"); nbomb.setUsdFor("威慑"); weapons.add(nbomb); J20 j20 = new J20(); j20.setDate(new Date()); j20.setName("歼20"); j20.setPerformance("性能优越"); j20.setUsdFor("战略打击"); weapons.add(j20); return weapons; } }
这样我们就达到了对于核弹头和飞机的不同的处理,访问非常简单。屏蔽了底层的处理。客户端根本不用理会底层是怎么样让他访问的,只需要按照访问者的规则去得到访问结果就可以了。
优点:符合单一职责原则,具有优秀的扩展性,灵活性非常高。