今天在使用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;
}
}
这样我们就达到了对于核弹头和飞机的不同的处理,访问非常简单。屏蔽了底层的处理。客户端根本不用理会底层是怎么样让他访问的,只需要按照访问者的规则去得到访问结果就可以了。
优点:符合单一职责原则,具有优秀的扩展性,灵活性非常高。