访问者模式
定义
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
解决
主要将数据结构与数据操作分离。
优点
- 增加新的操作很容易。因为增加新的操作意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
- 访问者模式是数据结构相对稳定的系统。
缺点
- 增加新的数据结构困难。
- 具体元素对访问者公布细节,违反了迪米特原则。
- 违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
结构
访问者模式包含如下角色:
- Visitor: 访问者类
- ConcreteVisitor: 具体访问者类
- Element: 元素类
- ConcreteElement: 具体元素类
- ObjectStructure: 对象结构类
实现
package visitorpattern;
/**
* 为该对象结构中ConcreteElement的每一个类声明一个Visit操作
*/
public abstract class Visitor {
public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}
/**
* 具体访问者,实现每个由Visitor生命的操作。每个操作实现算法一部分,而该算法片段乃是对应于结构中对象的类。
*/
class ConcreteVisitor1 extends Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println(concreteElementA.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println(concreteElementB.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
}
}
class ConcreteVisitor2 extends Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println(concreteElementA.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println(concreteElementB.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
}
}
package visitorpattern;
/**
* 定义一个Accept操作,它以一个访问者为参数
*/
public abstract class Element {
public abstract void accept(Visitor visitor);
}
/**
* 具体类,实现Accept操作
*/
class ConcreteElementA extends Element {
//利用双分派技术,实现处理与数据结构的分离
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementA(this);
}
//其他方法
public void operationA(){}
}
class ConcreteElementB extends Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementB(this);
}
//其他方法
public void operationB(){}
}
package visitorpattern;
import java.util.ArrayList;
import java.util.List;
/**
* 提供一个高层的接口以允许访问者访问它的元素
*/
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);
}
}
}
package visitorpattern;
public class VisitorClient {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure();
o.attach(new ConcreteElementA());
o.attach(new ConcreteElementB());
ConcreteVisitor1 visitor1 = new ConcreteVisitor1();
ConcreteVisitor2 visitor2 = new ConcreteVisitor2();
o.accept(visitor1);
o.accept(visitor2);
}
}
实例
package visitorpattern.demo;
//访问者
public abstract class Action {
//得到男人结论或反应
public abstract void getManConclusion(Man concreteElementA);
//得到女人结论或反应
public abstract void getWomanConclusion(Woman concreteElementA);
}
//成功状态
class Success extends Action
{
@Override
public void getManConclusion(Man m) {
System.out.println(m.getClass().getName()+this.getClass().getName()+"时,说:\"背后有一个伟大女人\"");
}
@Override
public void getWomanConclusion(Woman w) {
System.out.println(w.getClass().getName()+this.getClass().getName()+"时,说:\"背后有一个不成功的男人\"");
}
}
//失败状态
class Failing extends Action
{
@Override
public void getManConclusion(Man w) {
System.out.println(w.getClass().getName()+this.getClass().getName()+"时,说:\"闷头喝酒,谁也不用劝\"");
}
@Override
public void getWomanConclusion(Woman m) {
System.out.println(m.getClass().getName()+this.getClass().getName()+"时,说:\"眼泪汪汪,谁也劝不了\"");
}
}
//恋爱状态
class Loving extends Action
{
@Override
public void getManConclusion(Man m) {
System.out.println(m.getClass().getName()+this.getClass().getName()+"时,说:\"不懂装懂\"");
}
@Override
public void getWomanConclusion(Woman w) {
System.out.println(w.getClass().getName()+this.getClass().getName()+"时,说:\"懂也不懂\"");
}
}
package visitorpattern.demo;
//元素类的
public abstract class Person {
//接受
public abstract void accept(Action visitor);//用来获得'状态'对象的
}
/**
* 访问者模式的元素分类 是稳定的。那么这里只分为男人女人,这是重要的前提
*/
class Man extends Person
{
@Override
public void accept(Action visitor) {
visitor.getManConclusion(this);
}
}
class Woman extends Person
{
@Override
public void accept(Action visitor) {
visitor.getWomanConclusion(this);
}
}
package visitorpattern.demo;
import java.util.ArrayList;
import java.util.List;
/**
* "对象结构"设置该数据结构的方法
*/
public class ObjectStructure {
private List<Person> elements=new ArrayList<>();
//增加() ,
public void attach(Person element)
{
elements.add(element);
}
//删除()
public void detach(Person element)
{
elements.remove(element);
}
//显示()
public void display(Action visitor)
{
for (Person e : elements)
{
e.accept(visitor);
}
}
}
package visitorpattern.demo;
public class VisitorClient {
public static void main(String[] args) {
//在对象结构中加入要对比的”男人“和”女人“
ObjectStructure o= new ObjectStructure();
o.attach(new Man());
o.attach(new Woman());
//成功反应
Success s=new Success();
o.display(s);
//失败
Failing f=new Failing();
o.display(f);
//恋爱
Loving l=new Loving();
o.display(l);
}
}
总结
- 对象结构中对象对应的类,即元素分类很少改变,但经常需要在此对象结构上定义新的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
- 需要修改的就是ConcreteElement类和ObjectStructure类。Element类和其继承分类是稳定不变的。