访客设计模式示例

访客设计模式用于在不改变元素类的情况下,向对象结构添加新操作。该模式在对象结构中定义一个访问接口,让访问者访问并执行特定操作。在Java、JavaScript、Python等编程语言中,访客模式可用于在html标签中添加类或样式,而无需修改原有类。适用场景包括处理对象结构中多个不同类的操作,以及当需要频繁添加新操作时。
摘要由CSDN通过智能技术生成

本文是我们名为“ Java设计模式 ”的学院课程的一部分。

在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看

1引言

要了解访客设计模式,让我们重新访问“ 复合设计模式” 。 组合模式使您可以将对象组合成树形结构,以表示部分整体层次结构。

在Composite Pattern示例中,我们创建了一个由不同类型的对象组成的html结构。 现在假设我们需要在html标签中添加一个css类。 一种方法是在使用setStartTag方法添加开始标记时添加类。 但是,这种硬编码设置会给我们的代码造成僵化。

另一种方法是在父抽象HtmlTag类中添加新方法,例如addClass 。 所有子类都将重写此方法,并将提供css类。 这种方法的一个主要缺点是,如果有许多子类(将在大型html页面中显示),则在所有子类中实施此方法将变得非常昂贵且忙碌。 并假设,稍后我们需要在标签中添加另一个样式元素,我们再次需要执行相同的操作。

访客设计模式为您提供了一种在不更改元素类的情况下,在对象上添加新操作的方法,尤其是当操作经常更改时。

2.什么是访客设计模式

访客设计模式的目的是表示要对对象结构的元素执行的操作。 访问者可让您定义新操作,而无需更改其所操作元素的类。

当跨类层次结构的对象的异构集合设计操作时,Visitor模式很有用。 访客模式允许在不更改集合中任何对象的类的情况下定义操作。 为此,Visitor模式建议在一个单独的类中定义该操作,该类称为visiter类。 这将操作与其所操作的对象集合分开。 对于每个要定义的新操作,都会创建一个新的访问者类。 由于该操作将在一组对象上执行,因此访问者需要一种访问这些对象的公共成员的方法。 可以通过实施以下两个设计思想来满足此要求。

图1-访客设计模式类图

图1 –访客设计模式类图

游客

  • 为对象结构中每类ConcreteElement声明一个Visit操作。 操作的名称和签名标识了将Visit请求发送给visitor 。 这使访问者可以确定要访问的元素的具体类别。 然后,访问者可以直接通过其特定界面访问该元素。

具体访客

  • 实现Visitor声明的每个操作。 每个操作都会实现为结构中相应对象类别定义的算法片段。 ConcreteVisitor提供算法的上下文并存储其本地状态。 这种状态通常会在遍历结构期间累积结果。

元件

  • 定义将visitor作为参数的Accept操作。

ConcreteElement

  • 实现将visitor作为参数的Accept操作。

对象结构

  • 可以枚举其元素。
  • 可以提供一个高级界面,以允许visitor访问其元素。
  • 可以是组合或集合,例如列表或集合。

3.实施访客设计模式

为了实现Visitor设计模式,我们将使用相同的Composite Pattern代码 ,并将为其引入一些新的接口,类和方法。

实现Visitor模式需要两个重要的接口,一个Element接口,它将包含一个参数为Visitor类型的accept方法。 所有需要允许访问者访问的类都将实现此接口。 在我们的例子中, HtmlTag将实现Element接口,因为HtmlTag是所有具体html类的父抽象类,因此具体类将继承并覆盖Element接口的accept方法。

另一个重要的界面是Visitor界面; 此接口将包含带有实现Element接口的类的参数的visit方法。 还请注意,与Composite Design Pattern课程中显示的示例相反,我们在HtmlTag类中添加了两个新方法,即getStartTaggetEndTag

package com.javacodegeeks.patterns.visitorpattern;

public interface Element {
	
	public void accept(Visitor visitor);
}

package com.javacodegeeks.patterns.visitorpattern;

public interface Visitor {
	public void visit(HtmlElement element);
	public void visit(HtmlParentElement parentElement);
}

下面的代码来自Composite Pattern示例,进行了一些更改。

package com.javacodegeeks.patterns.visitorpattern;

import java.util.List;


public abstract class HtmlTag implements Element{
	
	public abstract String getTagName();
	public abstract void setStartTag(String tag);
	public abstract String getStartTag();
	public abstract String getEndTag();
	public abstract void setEndTag(String tag);
	public void setTagBody(String tagBody){
		throw new UnsupportedOperationException("Current operation is not support for this object");
	}
	public void addChildTag(HtmlTag htmlTag){
		throw new UnsupportedOperationException("Current operation is not support for this object");
	}
	public void removeChildTag(HtmlTag htmlTag){
		throw new UnsupportedOperationException("Current operation is not support for this object");
	}
	public List<HtmlTag>getChildren(){
		throw new UnsupportedOperationException("Current operation is not support for this object");
	}
	public abstract void generateHtml();

}

抽象的HtmlTag类实现Element接口。 下面的具体类将覆盖Element接口的accept方法,并调用visit方法,并将this运算符作为参数传递。 这将允许visitor方法获取该对象的所有公共成员,并对其添加新的操作。

package com.javacodegeeks.patterns.visitorpattern;

import java.util.ArrayList;
import java.util.List;

public class HtmlParentElement extends HtmlTag {

	private String tagName;
	private String startTag; 
	private String endTag;
	private List<HtmlTag>childrenTag;
	
	public HtmlParentElement(String tagName){
		this.tagName = tagName;
		this.startTag = "";
		this.endTag = "";
		this.childrenTag = new ArrayList<>();
	}
	
	@Override
	public String getTagName() {
		return tagName;
	}

	@Override
	public void setStartTag(String tag) {
		this.startTag = tag;
	}

	@Override
	public void setEndTag(String tag) {
		this.endTag = tag;
	}
	
	@Override
	public String getStartTag() {
		return startTag;
	}
	
	@Override
	public String getEndTag() {
		return endTag;
	}
	
	@Override
	public void addChildTag(HtmlTag htmlTag){
		childrenTag.add(htmlTag);
	}
	
	@Override
	public void removeChildTag(HtmlTag htmlTag){
		childrenTag.remove(htmlTag);
	}
	
	@Override
	public List<HtmlTag>getChildren(){
		return childrenTag;
	}

	@Override
	public void generateHtml() {
		System.out.println(startTag);
		for(HtmlTag tag : childrenTag){
			tag.generateHtml();
		}
		System.out.println(endTag);
		
	}

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

}
package com.javacodegeeks.patterns.visitorpattern;

public class HtmlElement extends HtmlTag{

	private String tagName;
	private String startTag; 
	private String endTag;
	private String tagBody;
	
	public HtmlElement(String tagName){
		this.tagName = tagName;
		this.tagBody = "";
		this.startTag = "";
		this.endTag = "";
	}
	
	@Override
	public String getTagName() {
		return tagName;
	}

	@Override
	public void setStartTag(String tag) {
		this.startTag = tag;
	}
	
	@Override
	public void setEndTag(String tag) {
		this.endTag = tag;
	}
	
	@Override
	public String getStartTag() {
		return startTag;
	}
	
	@Override
	public String getEndTag() {
		return endTag;
	}
	
	@Override
	public void setTagBody(String tagBody){
		this.tagBody = tagBody;
	}
	
	@Override
	public void generateHtml() {
		System.out.println(startTag+""+tagBody+""+endTag);
	}

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

}

现在,具体的访问者类:我们创建了两个具体的类,一个将为所有html标记添加一个cssvisitor ,另一个将使用html标记的style属性更改标记的宽度。

package com.javacodegeeks.patterns.visitorpattern;

public class CssClassVisitor implements Visitor{

	@Override
	public void visit(HtmlElement element) {
		element.setStartTag(element.getStartTag().replace(">", " class='visitor'>"));
		
	}

	@Override
	public void visit(HtmlParentElement parentElement) {
		parentElement.setStartTag(parentElement.getStartTag().replace(">", " class='visitor'>"));
	}

}
package com.javacodegeeks.patterns.visitorpattern;

public class StyleVisitor implements Visitor {

	@Override
	public void visit(HtmlElement element) {
		element.setStartTag(element.getStartTag().replace(">", " style='width:46px;'>"));
		
	}

	@Override
	public void visit(HtmlParentElement parentElement) {
		parentElement.setStartTag(parentElement.getStartTag().replace(">", " style='width:58px;'>"));
	}

}

现在,让我们测试上面的示例。

package com.javacodegeeks.patterns.visitorpattern;

public class TestVisitorPattern {
	
	public static void main(String[] args) {
		
		System.out.println("Before visitor......... \\n");
		
		HtmlTag parentTag = new HtmlParentElement("<html>");
		parentTag.setStartTag("<html>");
		parentTag.setEndTag("</html>");
		
		HtmlTag p1 = new HtmlParentElement("<body>");
		p1.setStartTag("<body>");
		p1.setEndTag("</body>");
		
		parentTag.addChildTag(p1);
		
		HtmlTag child1 = new HtmlElement("<p>");
		child1.setStartTag("<p>");
		child1.setEndTag("</p>");
		child1.setTagBody("Testing html tag library");
		p1.addChildTag(child1);
		
		child1 = new HtmlElement("<p>");
		child1.setStartTag("<p>");
		child1.setEndTag("</p>");
		child1.setTagBody("Paragraph 2");
		p1.addChildTag(child1);
		
		parentTag.generateHtml();
		
		System.out.println("\\nAfter visitor....... \\n");
		
		Visitor cssClass = new CssClassVisitor();
		Visitor style = new StyleVisitor();
		
		parentTag = new HtmlParentElement("<html>");
		parentTag.setStartTag("<html>");
		parentTag.setEndTag("</html>");
		parentTag.accept(style);
		parentTag.accept(cssClass);
		
		p1 = new HtmlParentElement("<body>");
		p1.setStartTag("<body>");
		p1.setEndTag("</body>");
		p1.accept(style);
		p1.accept(cssClass);
		
		parentTag.addChildTag(p1);
		
		child1 = new HtmlElement("<p>");
		child1.setStartTag("<p>");
		child1.setEndTag("</p>");
		child1.setTagBody("Testing html tag library");
		child1.accept(style);
		child1.accept(cssClass);
		
		p1.addChildTag(child1);
		
		child1 = new HtmlElement("<p>");
		child1.setStartTag("<p>");
		child1.setEndTag("</p>");
		child1.setTagBody("Paragraph 2");
		child1.accept(style);
		child1.accept(cssClass);
		
		p1.addChildTag(child1);
		
		parentTag.generateHtml();
	}

}

上面的代码将导致以下输出:

Before visitor......... 

<html>
<body>
<p>Testing html tag library</p>
<p>Paragraph 2</p>
</body>
</html>

After visitor....... 

<html style='width:58px;' class='visitor'>
<body style='width:58px;' class='visitor'>
<p style='width:46px;' class='visitor'>Testing html tag library</p>
<p style='width:46px;' class='visitor'>Paragraph 2</p>
</body>
</html>

“访问者之前……”之后的输出与“复合模式”课程中的输出相同。 后来,我们创建了两个具体的访问者,然后使用accept方法将它们添加到具体的html对象中。 输出“ After visitor…”显示给您结果,其中将css类和样式元素添加到html标记中。

请注意,Visitor Pattern的优点是我们可以在不更改其类的情况下向对象添加新操作。 例如,我们可以添加一些JavaScript函数,例如onclick或一些angularjs ng标签,而无需修改类。

4.何时使用访客设计模式

在以下情况下使用“访问者”模式:

  • 对象结构包含许多具有不同接口的对象类,并且您希望根据这些对象的具体类对这些对象执行操作。
  • 需要对对象结构中的对象执行许多不同且不相关的操作,并且您要避免使用这些操作“污染”它们的类。 访客可以通过在一个类中定义相关操作来将它们在一起。 当许多应用程序共享对象结构时,请使用Visitor将操作仅放在需要它们的那些应用程序中。
  • 定义对象结构的类很少更改,但是您经常想在该结构上定义新的操作。 更改对象结构类需要重新定义所有访问者的接口,这可能会导致成本高昂。 如果对象结构类经常更改,则最好在这些类中定义操作。

5. JDK中的访问者设计模式

  • javax.lang.model.element.Elementjavax.lang.model.element.ElementVisitor
  • javax.lang.model.type.TypeMirrorjavax.lang.model.type.TypeVisitor

6.下载源代码

这是“访客设计模式”的一课。 您可以在此处下载相关的源代码: VisitorPattern-Project

翻译自: https://www.javacodegeeks.com/2015/09/visitor-design-pattern.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值