访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的
前提下定义作用于这些元素的新操作。
即可以在不修改已有程序结构的前提下,通过添加额外的"访问者"来完成对已有代码功能的提升。
要点:
1.访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的
藕合解脱开,使得操作集合可以相对自由地演化。
2.访问者模式的目的是要把处理从数据结构分离出来。如果系统有比较稳定的数据结构,
又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得
算法操作的增加变得容易。
3.访问者模式的优点就是增加新的操作容易,因为增加新的操作就意味着增加一个新的访问方法。
访问者模式将有关的行为集中到一个访问者对象中。
4.访问者模式的缺点其实也就是使增加新的数据结构变得困难了。大多时候并不需要访问者模式,
但当一旦需要访问者模式时,那就是真的需要它了。事实上很难找到数据结构不变化的情况,
所以用访问者模式的机会就不太多了。
/**
* 访问者角色类:为该对象结构中具体元素角色声明一个访问操作接口。
* 该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。
* 这样访问者就可以通过该元素角色的特定接口直接访问它。
*/
public abstract class Visitor {
public abstract void visitConcreteElementA(ConcreteElementA elementA);
public abstract void visitConcreteElementB(ConcreteElementB elementB);
public abstract String getName();
}
/**
* 具体访问者角色:实现每个由访问者角色(Visitor)声明的操作。
*/
public class ConcreteVisitor1 extends Visitor{
@Override
public void visitConcreteElementA(ConcreteElementA elementA) {
System.out.println(elementA.getName()+" 被 "+this.getName()+" 访问。");
}
@Override
public void visitConcreteElementB(ConcreteElementB elementB) {
System.out.println(elementB.getName()+" 被 "+this.getName()+" 访问。");
}
@Override
public String getName() {
return "visitor1";
}
}
/**
* 具体访问者角色:实现每个由访问者角色(Visitor)声明的操作。
*/
public class ConcreteVisitor2 extends Visitor{
@Override
public void visitConcreteElementA(ConcreteElementA elementA) {
System.out.println(elementA.getName()+" 被 "+this.getName()+" 访问。");
}
@Override
public void visitConcreteElementB(ConcreteElementB elementB) {
System.out.println(elementB.getName()+" 被 "+this.getName()+" 访问。");
}
@Override
public String getName() {
return "visitor2";
}
}
/**
* Element类,定义一个accept操作,以一个访问者为参数。
*/
public abstract class Element {
public abstract void accept(Visitor visitor);
public abstract String getName();
}
/**
* 具体元素类A,实现accept操作。
*/
public class ConcreteElementA extends Element{
@Override
public void accept(Visitor visitor) {
// 充分利用双分派技术,实现处理与数据结构的分离
visitor.visitConcreteElementA(this);
}
// 其它的相关方法
public void operaitonA(){
}
@Override
public String getName() {
return "elementA";
}
}
/**
* 具体元素类B,实现accept操作。
*/
public class ConcreteElementB extends Element{
@Override
public void accept(Visitor visitor) {
// 充分利用双分派技术,实现处理与数据结构的分离
visitor.visitConcreteElementB(this);
}
// 其它的相关方法
public void operaitonA(){
}
@Override
public String getName() {
return "elementB";
}
}
/**
* 对象结构:这是使用访问者模式必备的角色。它要具备以下特征:
* 能枚举它的元素;
* 可以提供一个高层的接口以允许该访问者访问它的元素;
* 可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
*/
public class ObjectStructure {
private List<Element> elements = new ArrayList<Element>();
// 增加
public void attach(Element element){
elements.add(element);
}
// 移除
public void detach(Element element){
elements.remove(element);
}
// 查看显示
public void accept(Visitor visitor){
// 遍历调用
for(Element element:elements){
element.accept(visitor);
}
}
}
public class Main {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure();
o.attach(new ConcreteElementA());
o.attach(new ConcreteElementB());
ConcreteVisitor1 v1 = new ConcreteVisitor1();
ConcreteVisitor2 v2 = new ConcreteVisitor2();
o.accept(v1);
o.accept(v2);
}
}
输出结果如下:
elementA 被 visitor1 访问。
elementB 被 visitor1 访问。
elementA 被 visitor2 访问。
elementB 被 visitor2 访问。
访问者模式实例:(男人和女人不同状态下的反应)
/**
* 状态抽象类,人只分为男人和女人,性别的分类是稳定的,所以可以在状态类中,增加
* "男人反应"和"女人反应"两个方法,方法个数是稳定的,不会很容易的发生变化。
*/
public abstract class Action {
// 得到男人的结论或反应
public abstract void getManConclusion(PersonMan man);
// 得到女人的结论或反应
public abstract void getWomanConclusion(PersonWoman woman);
// 获取状态名称
public abstract String getName();
}
/**
* 成功状态,每一种具体的状态都继承"状态"抽象类,实现两个反应的方法。
*/
public class ActionSuccess extends Action{
@Override
public void getManConclusion(PersonMan man) {
System.out.println(man.getName()+" "+this.getName()
+" 时,背后多半有一个伟大的女人。");
}
@Override
public void getWomanConclusion(PersonWoman woman) {
System.out.println(woman.getName()+" "+this.getName()
+" 时,背后多半有一个不成功的男人。");
}
@Override
public String getName() {
return "成功";
}
}
/**
* 失败状态,每一种具体的状态都继承"状态"抽象类,实现两个反应的方法。
*/
public class ActionFail extends Action{
@Override
public void getManConclusion(PersonMan man) {
System.out.println(man.getName()+" "+this.getName()
+" 时,闷头喝酒,谁也不用劝。");
}
@Override
public void getWomanConclusion(PersonWoman woman) {
System.out.println(woman.getName()+" "+this.getName()
+" 时,眼泪汪汪,谁也劝不了。");
}
@Override
public String getName() {
return "失败";
}
}
/**
* 恋爱状态,每一种具体的状态都继承"状态"抽象类,实现两个反应的方法。
*/
public class ActionLove extends Action{
@Override
public void getManConclusion(PersonMan man) {
System.out.println(man.getName()+" "+this.getName()
+" 时,凡事不懂也要装懂。");
}
@Override
public void getWomanConclusion(PersonWoman woman) {
System.out.println(woman.getName()+" "+this.getName()
+" 时,遇事懂也装作不懂。");
}
@Override
public String getName() {
return "恋爱";
}
}
/**
* 人的抽象类
*/
public abstract class Person {
// 接受抽象方法,vistor是用来获得"状态"对象的
public abstract void accept(Action vistor);
// 获取人的类别名称
public abstract String getName();
}
/**
* 男人类
*/
public class PersonMan extends Person{
@Override
public void accept(Action vistor) {
/** 1.首先在客户程序中将具体状态作为参数传递给"男人"类完成了一次分派,
* 然后"男人"类调用作为参数的"具体状态"中的方法的"男人反应",同时将自己(this)作为参数传递进去,
* 这便完成了第二次分派。
* 2.双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型,"接受"方法得到执行的操作
* 不仅决定于"状态"类的具体状态,还决定于它访问的'人'的类别。
*/
vistor.getManConclusion(this);
}
@Override
public String getName() {
return "男人";
}
}
/**
* 女人类
*/
public class PersonWoman extends Person{
@Override
public void accept(Action vistor) {
vistor.getWomanConclusion(this);
}
@Override
public String getName() {
return "女人";
}
}
/**
* 对象结构类,针对不同的"状态"遍历"男人"和"女人",得到不同的反应。
*/
public class ObjectStructure {
private List<Person> persons = new ArrayList<Person>();
// 增加
public void attach(Person person){
persons.add(person);
}
// 移除
public void detach(Person person){
persons.remove(person);
}
// 查看显示
public void display(Action visitor){
// 遍历调用
for(Person person:persons){
person.accept(visitor);
}
}
}
public class Main {
public static void main(String[] args) {
// 在对象结构中加入到对比的男人和女人
ObjectStructure o = new ObjectStructure();
o.attach(new PersonMan());
o.attach(new PersonWoman());
// 成功状态下男人和女人的反应
ActionSuccess success = new ActionSuccess();
o.display(success);
// 失败状态下男人和女人的反应
ActionFail fail = new ActionFail();
o.display(fail);
// 恋爱状态下男人和女人的反应
ActionLove love = new ActionLove();
o.display(love);
}
}
输出结果如下:
男人 成功 时,背后多半有一个伟大的女人。
女人 成功 时,背后多半有一个不成功的男人。
男人 失败 时,闷头喝酒,谁也不用劝。
女人 失败 时,眼泪汪汪,谁也劝不了。
男人 恋爱 时,凡事不懂也要装懂。
女人 恋爱 时,遇事懂也装作不懂。