Java中怎么用Reflection實(shí)現(xiàn)Visitor模式-創(chuàng)新互聯(lián)

這篇文章主要為大家展示了“Java中怎么用Reflection實(shí)現(xiàn)Visitor模式”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Java中怎么用Reflection實(shí)現(xiàn)Visitor模式”這篇文章吧。

超過十余年行業(yè)經(jīng)驗,技術(shù)領(lǐng)先,服務(wù)至上的經(jīng)營模式,全靠網(wǎng)絡(luò)和口碑獲得客戶,為自己降低成本,也就是為客戶降低成本。到目前業(yè)務(wù)范圍包括了:網(wǎng)站設(shè)計、網(wǎng)站建設(shè),成都網(wǎng)站推廣,成都網(wǎng)站優(yōu)化,整體網(wǎng)絡(luò)托管,微信平臺小程序開發(fā),微信開發(fā),App定制開發(fā),同時也可以讓客戶的網(wǎng)站和網(wǎng)絡(luò)營銷和我們一樣獲得訂單和生意!

集合(Collection)普遍應(yīng)用于面向?qū)ο缶幊讨?,但它也?jīng)常引發(fā)一些和代碼有關(guān)的疑問。例如,"如果一個集合存在不同的對象,該如何對它執(zhí)行操作?"

一種方法是,對集合中的每個元素進(jìn)行迭代,然后基于所在的類,對每個元素分別執(zhí)行對應(yīng)的操作。這會很難辦,特別是,如果你不知道集合中有什么類型的對象。例如,假設(shè)想打印集合中的元素,你可以寫出如下的一個方法(method):

public void messyPrintCollection(Collection collection) {
  Iterator iterator = collection.iterator()
  while (iterator.hasNext())
  System.out.println(iterator.next().toString())
}

這看起來夠簡單的了。它只不過調(diào)用了object.toString()方法,然后打印出對象,對嗎?但如果有一組哈希表怎么辦?事情就會開始變得復(fù)雜起來。你必須檢查從集合中返回的對象的類型:

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());
  }
}

不錯,現(xiàn)在已經(jīng)解決了嵌套集合的問題,但它需要對象返回String,如果有其它不返回String的對象存在怎么辦?如果想在String對象前后添加引號以及在Float后添加f又該怎么辦?代碼還是越來越復(fù)雜:

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());
  }
}

可以看到,事情的復(fù)雜度會急劇增長。你當(dāng)然不想讓一段代碼到處充斥著if-else語句!那怎么避免呢?Visitor模式可以幫你。

要實(shí)現(xiàn)Visitor模式,得為訪問者建立一個Visitor接口,還要為被訪問的集合建立一個Visitable接口。然后,讓具體類實(shí)現(xiàn)Visitor和Visitable接口。這兩個接口如下所示:

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);
}

對于具體的String,可能是這樣:

public class VisitableString implements Visitable
{
  private String value;
  public VisitableString(String string) {
  value = string;
  }
  public void accept(Visitor visitor) {
  visitor.visitString(this);
  }
}

在accept方法中,對this類型調(diào)用正確的visitor方法:

visitor.visitString(this)

這樣,就可以如下實(shí)現(xiàn)具體的Visitor:

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");
  }
}

實(shí)現(xiàn)VisitableFloat和VisitableCollection類的時候,它們也是各自調(diào)用合適的Visitor方法,所得到的效果和前面那個用了if-else的messyPrintCollection方法一樣,但這里的手法更干凈。在visitCollection()中,調(diào)用的是Visitable.accept(this),然后這個調(diào)用又返回去調(diào)用一個合適的Visitor方法。這被稱做 "雙分派";即,Visitor先調(diào)用了Visitable類中的方法,這個方法又回調(diào)到Visitor類中。

雖然通過實(shí)現(xiàn)visitor消除了if-else語句,卻也增加了很多額外的代碼。最初的String和Float對象都要用實(shí)現(xiàn)了Visitable接口的對象進(jìn)行包裝。這有點(diǎn)討厭,但一般說來不是問題,因為你可以讓經(jīng)常被訪問的集合只包含那些實(shí)現(xiàn)了Visitable接口的對象。

但似乎這還是額外的工作。更糟糕的是,當(dāng)增加一個新的Visitable類型如VisitableInteger時,會發(fā)生什么呢?這是Visitor模式的一個重大缺陷。如果想增加一個新的Visitable對象,就必須修改Visitor接口,然后對每一個Visitor實(shí)現(xiàn)類中的相應(yīng)的方法一一實(shí)現(xiàn)。你可以用一個帶缺省空操作的Visitor抽象基類來代替接口。那就很象Java GUI中的Adapter類。那個方法的問題在于,它需要占用單繼承;而你往往想保留單繼承,讓它用于其它什么東西,比如繼承StringWriter。那個方法還有限制,它只能夠成功訪問Visitable對象。

幸運(yùn)的是,Java可以讓Visitor模式更靈活,使得你可以隨心所欲地增加Visitable對象。怎么做?答案是,使用Reflection。比如,可以設(shè)計這樣一個ReflectiveVisitor接口,它只需要一個方法:

public interface ReflectiveVisitor {
  public void visit(Object o);
}

就這樣,很簡單。至于Visitable,還是和前面一樣,我過一會兒再說?,F(xiàn)在先用Reflection來實(shí)現(xiàn)PrintVisitor:

public class PrintVisitor implements ReflectiveVisitor {
  public void visitCollection(Collection collection)
  { ... same as above ... }
  public void visitString(String string)
  { ... same as above ... }
  public void visitFloat(Float float)
  { ... same as above ... }

  public void default(Object o)
  {
  System.out.println(o.toString());
  }

  public void visit(Object o) {
  // Class.getName() returns package information as well.
  // This strips off the package information giving us
  // just the class name
  String methodName = o.getClass().getName();
  methodName = "visit"+
  methodName.substring(methodName.lastIndexOf('.')+1);
  // Now we try to invoke the method visit
  try {
  // Get the method visitFoo(Foo foo)
  Method m = getClass().getMethod(methodName,
  new Class[] { o.getClass() });
  // Try to invoke visitFoo(Foo foo)
  m.invoke(this, new Object[] { o });
  } catch (NoSuchMethodException e) {
  // No method, so do the default implementation
  default(o);
  }
  }
}

現(xiàn)在不需要Visitable包裝類。僅僅只是調(diào)用visit(),請求就會分發(fā)到正確的方法上。很不錯的一點(diǎn)是,只要認(rèn)為適合,visit()就可以分發(fā)。這并非必須使用reflection--它可以使用其它完全不同的機(jī)制。

新的PrintVisitor中,有針對Collection,String和Float而寫的方法,但然后它又在catch語句中捕捉所有未處理的類型。你要擴(kuò)展visit()方法,使得它也能夠處理所有的父類。首先,得增加一個新方法,稱為getMethod(Class c),它返回的是要調(diào)用的方法;為了找到這個相匹配的方法,先在類c的所有父類中尋找,然后在類c的所有接口中尋找。

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;
}

看起來有些復(fù)雜,其實(shí)不然。實(shí)際上,它只是根據(jù)傳進(jìn)來的類名去尋找相應(yīng)的方法而已。如果沒找到,就在父類中找;還沒找到,再到接口中找。最后,就拿visitObject()作為缺省。

注意,為了照顧那些熟悉傳統(tǒng)Visitor模式的讀者,我對方法的名稱采用了傳統(tǒng)的命名方式。但正如你們一些人所注意到的,把所有的方法命名為 "visit" 然后讓參數(shù)類型作為區(qū)分會更高效。但這樣做的話,你得把主visit(Object o)方法的名字改為dispatch(Object o)之類。否則,就沒有一個缺省方法可用了,你就得在調(diào)用visit(Object o)時將類型轉(zhuǎn)換為Object,以保證visit采用的是正確的調(diào)用方式。

現(xiàn)在可以修改visit()方法,以利用getMethod():

public void visit(Object object) {
  try {
  Method method = getMethod(getClass(), object.getClass());
  method.invoke(this, new Object[] {object});
  } catch (Exception e) { }
}

現(xiàn)在,visitor對象的功能強(qiáng)大多了。你可以傳進(jìn)任何對象,并且有某個方法處理它。另外一個好處是,還有一個缺省方法visitObject(Object o),它可以捕捉任何未知的對象。再多花點(diǎn)工夫,你還可以寫出一個visitNull()方法。

我在上面對Visitable接口避而不談自有原因。傳統(tǒng)Visitor模式的另一個好處是,它允許Visitable對象來控制對對象結(jié)構(gòu)的訪問。例如,假設(shè)有一個實(shí)現(xiàn)了Visitable的TreeNode對象,你可以讓一個accept()方法來遍歷它的左右節(jié)點(diǎn):

public void accept(Visitor visitor) {
  visitor.visitTreeNode(this);
  visitor.visitTreeNode(leftsubtree);
  visitor.visitTreeNode(rightsubtree);
}

這樣,只用對Visitor類再進(jìn)行一點(diǎn)修改,就可以進(jìn)行Visitable控制訪問:

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);
}

如果已經(jīng)實(shí)現(xiàn)了一個Visitable對象結(jié)構(gòu),可以保留callAccept()方法并使用Visitable控制訪問。如果想在visitor中訪問結(jié)構(gòu),只需改寫callAccept()方法,使之什么也不做。

想讓數(shù)個不同的訪問者對同一個對象集合進(jìn)行訪問時,Visitor模式可以發(fā)揮它的強(qiáng)大作用。假設(shè)已經(jīng)有一個解釋器,一個中綴寫作器,一個后綴寫作器,一個XML寫作器和一個sql寫作器,它們都作用在同一個對象集合上。那么,也可以很容易地為相同的對象集合寫出一個前綴寫作器和一個SOAP寫作器。另外,這些寫作器可以正常地和它們所不知道的對象工作;當(dāng)然,如果愿意,也可以讓它們拋出異常。

以上是“Java中怎么用Reflection實(shí)現(xiàn)Visitor模式”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道!

當(dāng)前標(biāo)題:Java中怎么用Reflection實(shí)現(xiàn)Visitor模式-創(chuàng)新互聯(lián)
轉(zhuǎn)載來源:http://bm7419.com/article14/ijcge.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊、外貿(mào)建站網(wǎng)站收錄、網(wǎng)站設(shè)計、網(wǎng)站內(nèi)鏈、標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)