📄 visitorpattern.htm
字号:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="css/stdlayout.css" type="text/css">
<link rel="stylesheet" href="css/print.css" type="text/css">
<meta content="text/html; charset=gb2312" http-equiv="content-type">
<title>Visitor 模式</title>
</head>
<body>
<h3><a href="http://caterpillar.onlyfun.net/GossipCN/index.html">From
Gossip@caterpillar</a></h3>
<h1><a href="CppGossip.html">Design Pattern: Visitor 模式</a></h1>
在Java中所有的物件都继承自Object物件,这样作的优点之一,就是使得一些集合物件的资料结构容易管理,例如您可以将任何型态的物件放入Vector中。<br>
<br>
然而现在有个问题是,如果您的集合(connection)物件中不仅储存一种型态的物件,如果想要对这些物件作出一些个别化的操作,首要条件就是要知道该物件的型态,使用 instanceof 似乎是个不错的方式,在程式简单的情况下,也许您会这么作:<br>
<div style="margin-left: 40px;"><span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> public class ElementA { </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> // some implementing </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> } </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> public class ElementB { </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> // some implementing </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> } </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> public class ElementC { </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> // some implementing </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> } </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> // ...... </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> Iterator iterator = arrayList.iterator() </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> while (iterator.hasNext()) { </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> if (o instanceof ElementA) </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> (ElementA) o.operationA(); </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> else if (o instanceof ElementB) </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> (ElementB) o.operationB(); </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> else if (o instanceof ElementC) </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> (ElementC) o.operationC(); </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> else </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> System.out.println(</span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;">
"Sorry! I don't know who you are! " </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> + o.toString()); </span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> //....</span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> }</span><br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<br style="font-family: Courier New,Courier,monospace; font-weight: bold;">
<span style="font-family: Courier New,Courier,monospace; font-weight: bold;"> //....</span><br>
</div>
<br>
这么作并不是不可以,只是将来的扩充性不大,如果今天您想要一次改变对每一种类型物件的操作,您必须修改很多地方。<br>
<br>
从物件自身的角度来想好了,物件在一个个的房子中,物件说:“不要在房子外费尽心思判断了,即然您不知道我是谁,那么您就进来访问我好了,我告诉您我是谁,这么一来您就知道如何操作我了!”<br>
<br>
用程式来实现上面这个描述:<br>
<ul>
<li> IElement.java </li>
</ul>
<pre>public interface IElement { <br> public void accept(IVisitor visitor); <br>} <br></pre>
<br>
<ul>
<li> ElementA.java </li>
</ul>
<pre>public class ElementA implements IElement { <br> public void accept(IVisitor visitor) { <br> visitor.visit(this); <br> }<br><br> public void operationA() { <br> System.out.println(<br> "do A's job....such-and-such...."); <br> } <br>} <br></pre>
<br>
<ul>
<li> ElementB.java </li>
</ul>
<pre>public class ElementB implements IElement { <br> public void accept(IVisitor visitor) { <br> visitor.visit(this); <br> }<br><br> public void operationB() { <br> System.out.println(<br> "do B's job....such-and-such...."); <br> }<br>} <br></pre>
<br>
<ul>
<li> ElementC.java </li>
</ul>
<pre>public class <span class="createlink">ElementC</span> implements <span class="createlink">IElement</span> { <br> public void accept(<span class="createlink">IVisitor</span> visitor) { <br> visitor.visit(this); <br> }<br><br> public void operationC() { <br> System.out.println(<br> "do C's job....such-and-such...."); <br> } <br>} <br></pre>
<br>
<ul>
<li> IVisitor.java </li>
</ul>
<pre>public interface IVisitor { <br> public void visit(ElementA element); <br> public void visit(ElementB element); <br> public void visit(ElementC element); <br>} <br></pre>
<br>
<ul>
<li> VisitorA.java </li>
</ul>
<pre>public class VisitorA implements IVisitor { <br> public void visit(ElementA element) { <br> element.operationA(); <br> }<br><br> public void visit(ElementB element) { <br> element.operationB(); <br> }<br><br> public void visit(ElementC element) { <br> element.operationC(); <br> } <br>} <br></pre>
<br>
<ul>
<li> Main.java </li>
</ul>
<pre>public class Main { <br> public static void main(String[] args) { <br> // know nothing about their type <br> // after storing them into Element array <br> IElement[] list = {new ElementA(), <br> new ElementB(), <br> new ElementC()}; <br><br> IVisitor visitor = new VisitorA();<br><br> for (int i=0; i < list.length; i++) <br> list[i].accept(visitor); <br> } <br>} </pre>
<br>
Visitor访问是基于overload来完成,对于每一个实现IElement的物件来说,它接受IVisitor来访问它,在accept()方法
中,IVisitor使用正确的方法来访问IElement(显然的,这么部份可以靠不同的函式名称,或是overload来达成),并在visit()
中对IElement作出对应的操作,如果您今天想要换掉每一个IElement的操作,只要更换IVisitor类型的物件就可以了,也就是这行:<br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> // IVisitor visitor = new VisitorA(); </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> // 换掉一个IVisitor,就可以换掉所有的操作</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> // 不用修改多个地方</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> IVisitor visitor = new VisitorB(); </span><br>
</div>
<br>
<br>
举个实际的例子,假设VisitorA只是个懒惰的推销员好了,今天有一个比较勤快的推销员VisitorB,在访问过IElement之后,会对 IElement作出更多的操作,要在程式中实现VisitorB,只要增加一个VisitorB类别就可以了:<br>
<ul>
<li> VisitorB.java </li>
</ul>
<pre>public class VisitorB implements IVisitor { <br> public void visit(ElementA element) { <br> System.out.println("VisitorB is a hard worker...."); <br> element.operationA(); <br> System.out.println(<br> "I want to do some extra work on A...."); <br> }<br><br> public void visit(ElementB element) { <br> System.out.println("VisitorB is a hard worker...."); <br> element.operationB(); <br> System.out.println(<br> "I want to do some extra work on B...."); <br> }<br><br> public void visit(ElementC element) { <br> System.out.println("VisitorB is a hard worker...."); <br> element.operationC(); <br> System.out.println(<br> "I want to do some extra work on C...."); <br> } <br>} <br></pre>
<br>
改一下Main来示范:<br>
<ul>
<li> Main.java </li>
</ul>
<pre>public class Main { <br> public static void main(String[] args) { <br> IElement[] list = {new ElementA(), <br> new ElementB(), <br> new ElementC()}; <br><br> System.out.println("visitorA is coming......."); <br> IVisitor visitorA = new VisitorA(); <br> for (int i=0; i < list.length; i++) <br> list[i].accept(visitorA);<br><br> System.out.println("\nvisitorB is coming......."); <br> IVisitor visitorB = new VisitorB(); <br> for (int i=0; i < list.length; i++) <br> list[i].accept(visitorB); <br> } <br>} <br></pre>
<br>
在范例中的System.out.println();只是个示意,它也可能是您对IElement的额外方法的直接调用。 <br>
<br>
Visitor模式的 UML 结构类图如下:<br>
<div style="text-align: center;"><img style="width: 544px; height: 430px;" alt="Visitor" title="Visitor" src="images/visitor-1.jpg"><br>
<br>
</div>
在<a href="http://www.javaworld.com">Java World</a>中有一篇文章,提到可以利用reflection来改进使用访问者模式时的弹性,有兴趣的可以进一步参考一下<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip98.html">Reflect on the Visitor design pattern</a>。<br>
<br>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -