设计模式——访问者模式

本文介绍访问者模式。

概念

为一个对象结构增加新能力。

结构和工作原理

  • Visitor:抽象访问者,抽象访问者对每一个具体对元素——ConcreteElement定义一个访问操作,从这个操作的名称或者参数声明操作具体元素对类型
  • ConcreteVisitor:具体访问者,是抽象访问者对子类,实现了抽象访问者定义对方法,每个操作访问对象结构中一种类型的角色
  • ObjectStructure:对象结构,是元素的集合,用于存放元素和提供对元素对访问
  • Element:抽象元素,一般为接口或者抽象类,定义了accept方法用于接受访问者
  • ConcreteElement:具体元素,实现或者继承Element,实现了accpet方法,在该方法中调用具体Visitor的访问方法完成对本元素的操作

访问者模式适用于需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

访问者模式,可以针对具体的元素的属性做不同的处理,具体元素中可能会存在各种属性,需要执行不同的操作,且这些操作在职责上并不属于具体元素,为了避免职责上面的混乱,可以为每一种处理定义专门的处理类即ConcreteVisitor,元素定义接受Visitor的方法(accept)并调用Visitor中的具体方法,同时由于将具体操作封装在了访问者中,可以动态的新增访问者以达到新增访问操作的目的。在某些情况下,为了执行具体的操作,访问者可能需要知道具体元素内部的方法,这样会违反面向对象的设计思想。

优点:

  • 新增修改访问的具体实现十分方便,符合开闭原则
  • Element无关的具体执行逻辑封装在第三者Visitor中,符合职责单一原则

缺点:

  • 新增新的Element是需要在没一个类中新增相应的调用访问者的方法
  • Element可能会暴露自身的属性方法给Visitor,破坏了Element本身的封装性

代码示例

public interface Judge {

void visitPlaintiff(Plaintiff plaintiff);

void visitDefendant(Defendant defendant);
}
@Slf4j
public class EvidenceJudge implements Judge {

@Override
public void visitPlaintiff(Plaintiff plaintiff) {
log.info("EvidenceJudge- visitPlaintiff- {}", plaintiff.getEvidence());
}

@Override
public void visitDefendant(Defendant defendant) {
log.info("EvidenceJudge- visitDefendant- {}", defendant.getEvidence());
}
}
@Slf4j
public class TestimonyJudge implements Judge {

@Override
public void visitPlaintiff(Plaintiff plaintiff) {
log.info("TestimonyJudge- visitPlaintiff- {}", plaintiff.getTestimony());
}

@Override
public void visitDefendant(Defendant defendant) {
log.info("TestimonyJudge- visitDefendant- {}", defendant.getTestimony());
}
}

@Getter
@Setter
public abstract class Litigant {

protected String evidence;

protected String Testimony;

public Litigant(String evidence, String testimony) {
this.evidence = evidence;
this.Testimony = testimony;
}

abstract void accept(Judge judge);
}
public class Plaintiff extends Litigant {

public Plaintiff(String evidence, String testimony) {
super(evidence, testimony);
}

@Override
void accept(Judge judge) {
judge.visitPlaintiff(this);
}
public class Defendant extends Litigant {

public Defendant(String evidence, String testimony) {
super(evidence, testimony);
}

@Override
void accept(Judge judge) {
judge.visitDefendant(this);
}
}
public class LitigantGroup {

private List<Litigant> litigantList = new ArrayList<>();

public void addLitigant(Litigant litigant) {
this.litigantList.add(litigant);
}

public void accept(Judge judge) {
litigantList.forEach(litigant -> litigant.accept(judge));
}
}
public class VisitorClient {

public static void main(String[] args) {
LitigantGroup group = new LitigantGroup();
Plaintiff plaintiff = new Plaintiff("plaintiff_evidence", "plaintiff_testimony");
Defendant defendant = new Defendant("defendant_evidence", "defendant_testimony");
group.addLitigant(plaintiff);
group.addLitigant(defendant);

EvidenceJudge evidenceJudge = new EvidenceJudge();
TestimonyJudge testimonyJudge = new TestimonyJudge();
group.accept(evidenceJudge);
group.accept(testimonyJudge);
}
}

参考资料

https://juejin.im/entry/5ab4c3d65188251fc3293550
https://www.runoob.com/design-pattern/visitor-pattern.html

Author: nopainanymore
Link: http://nopainanymore.me/DesignPattern-Vistor/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
wechat