集合類型在面對物件程式中很常用,這也帶來一些程式碼相關的問題。 」
一種做法就是遍歷集合中的每個元素,然後根據它的類型而做具體的操作。這會很複雜,尤其當你不知道集合中元素的類型時。如果y要列印集合中的元素,可以寫一個這樣的方法:
public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) System.out.println(iterator.next().toString()) }
看起來很簡單。包含hashtable的vector呢?字串
不是你想要的呢? ##public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Collection) messyPrintCollection((Collection)o); else System.out.println(o.toString()); } }
程式碼很快就變得雜亂了。 ##為實作訪客模式,你需要建立一個Visitor介面,為被存取的集合物件建立一個Visitable介面。大致如下:public void messyPrintCollection(Collection collection) {
Iterator iterator = collection.iterator()
while (iterator.hasNext()) {
Object o = iterator.next();
if (o instanceof Collection)
messyPrintCollection((Collection)o);
else if (o instanceof String)
System.out.println("'"+o.toString()+"'");
else if (o instanceof Float)
System.out.println(o.toString()+"f");
else
System.out.println(o.toString());
}
}
public interface Visitor { public void visitCollection(Collection collection); public void visitString(String string); public void visitFloat(Float float); } public interface Visitable { public void accept(Visitor visitor); }
在accept方法中,根據不同的類型,呼叫visitor中對應的方法:
#visitor.visitString(this)public class VisitableString implements Visitable { private String value; public VisitableString(String string) { value = string; } public void accept(Visitor visitor) { visitor.visitString(this); } }
到時候,只要實作了VisitableFloat類別和VisitableCollection類別並呼叫適當的visitor方法,你就可以去掉包含一堆if-else結構的messyPrintCollection方法,採用十分清爽的方式實作了相同的功能。呼叫了visitor中正確的方法。語句不見了,但還是引入了很多附加的程式碼。你可以限制被存取集合只能包含Visitable物件。
然而,這還有很多附加的工作要做。更糟的是,當你想增加一個新的Visitable類型時怎麼辦,例如VisitableInteger?這是訪客模式的一個主要缺點。如果你想要增加一個新的Visitable類型,你必須改變Visitor介面以及每個實作Visitor介面方法的類別。你可以不把Visitor設計為接口,取而代之,可以把Visitor設計為一個有
空運算的抽象基底類別。這與Java GUI中的Adapter類別很相似。這麼做的問題是你會用盡單次繼承
,而常見的情形是你還想用繼承實作其他功能,像是繼承StringWriter類別。這同樣只能成功存取實作Visitable介面的物件。
幸運的是,Java可以讓你的訪客模式更靈活,你可以依照你的意願增加Visitable物件。怎麼實現呢?答案是使用反射。使用反射的ReflectiveVisitor介面只需要一個方法:
public class PrintVisitor implements Visitor { public void visitCollection(Collection collection) { Iterator iterator = collection.iterator(); while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Visitable) ((Visitable)o).accept(this); } public void visitString(String string) { System.out.println("'"+string+"'"); } public void visitFloat(Float float) { System.out.println(float.toString()+"f"); } }
好了,上面很簡單。 Visitable介面先不動,我會說。現在,我使用反射實作PrintVisitor類別。
public interface ReflectiveVisitor { public void visit(Object o); }
現在你不需要使用Visitable包裝類別(包裝了原始型別String、Float)。你可以直接存取visit(),它會呼叫正確的方法。 visit()的一個優點是它會分派它認為合適的方法。這不一定使用反射,可以使用完全不同的一種機制。
在新的PrintVisitor類別中,有對應於Collections、String和Float的操作方法;對於不能處理的類型,可以透過catch語句捕捉。對於不能處理的類型,可以透過擴充visit()方法來嘗試處理它們的所有超類別。首先,增加一個新的方法getMethod(Class c),回傳值就是一個可被觸發的方法。它會搜尋Class c的所有父類別和接口,以找到一個匹配方法。 这看上去很复杂,实际上并不。大致来说,首先根据传入的class名称搜索可用方法;如果没找到,就尝试从父类搜索;如果还没找到,就从接口中尝试。最后,(仍没找到)可以使用visitObject()作为默认方法。 由于大家对传统的访问者模式比较熟悉,这里沿用了之前方法命名的惯例。但是,有些人可能注意到,把所有的方法都命名为“visit”并通过参数类型不同来区分,这样更高效。然而,如果你这么做,你必须把visit(Object o)方法的名称改为其他,比如dispatch(Object o)。否则,(当没有对应处理方法时),你无法退回到默认的处理方法,并且当你调用visit(Object o)方法时,为了确保正确的方法调用,你必须将参数强制转化为Object。 为了利用getMethod()方法,现在需要修改一下visit()方法。 现在,visitor类更加强大了——可以传入任意的对象并且有对应的处理方法。另外,有一个默认处理方法,visitObject(Object o),的好处就是就可以捕捉到任何没有明确说明的类型。再稍微修改下,你甚至可以添加一个visitNull()方法。 我仍保留Visitable接口是有原因的。传统访问者模式的另一个好处是它可以通过Visitable对象控制对象结构的遍历顺序。举例来说,假如有一个实现了Visitable接口的类TreeNode,它在accept()方法中遍历自己的左右节点。 这样,只要修改下Visitor类,就可以通过Visitable类控制遍历: 如果你实现了Visitable对象的结构,你可以保持callAccept()不变,就可以使用Visitable控制的对象遍历。如果你想在visitor中遍历对象结构,你只需重写allAccept()方法,让它什么都不做。 当使用几个不同的visitor去操作同一个对象集合时,访问者模式的力量就会展现出来。比如,当前有一个解释器、中序遍历器、后续遍历器、XML编写器以及SQL编写器,它们可以处理同一个对象集合。我可以轻松地为这个集合再写一个先序遍历器或者一个SOAP编写器。另外,它们可以很好地兼容它们不识别的类型,或者我愿意的话可以让它们抛出异常。 使用Java反射,可以使访问者模式提供一种更加强大的方式操作对象结构,可以按照需求灵活地增加新的 以上是Java實作在訪客模式中使用反射的範例程式碼分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!protected Method getMethod(Class c) {
Class newc = c;
Method m = null;
// Try the superclasses
while (m == null && newc != Object.class) {
String method = newc.getName();
method = "visit" + method.substring(method.lastIndexOf('.') + 1);
try {
m = getClass().getMethod(method, new Class[] {newc});
} catch (NoSuchMethodException e) {
newc = newc.getSuperclass();
}
}
// Try the interfaces. If necessary, you
// can sort them first to define 'visitable' interface wins
// in case an object implements more than one.
if (newc == Object.class) {
Class[] interfaces = c.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
String method = interfaces[i].getName();
method = "visit" + method.substring(method.lastIndexOf('.') + 1);
try {
m = getClass().getMethod(method, new Class[] {interfaces[i]});
} catch (NoSuchMethodException e) {}
}
}
if (m == null) {
try {
m = thisclass.getMethod("visitObject", new Class[] {Object.class});
} catch (Exception e) {
// Can't happen
}
}
return m;
}
public void visit(Object object) {
try {
Method method = getMethod(getClass(), object.getClass());
method.invoke(this, new Object[] {object});
} catch (Exception e) { }
}
public void accept(Visitor visitor) {
visitor.visitTreeNode(this);
visitor.visitTreeNode(leftsubtree);
visitor.visitTreeNode(rightsubtree);
}
public void visit(Object object) throws Exception
{
Method method = getMethod(getClass(), object.getClass());
method.invoke(this, new Object[] {object});
if (object instanceof Visitable)
{
callAccept((Visitable) object);
}
}
public void callAccept(Visitable visitable) {
visitable.accept(this);
}
总结
Visitable
类型。我希望在你的编程之旅中可以使用访问者模式。