国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

用Reflection實(shí)現(xiàn)Visitor模式

2019-11-18 12:17:23
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  Visitor模式的常用之處在于,它將對(duì)象集合的結(jié)構(gòu)和對(duì)集合所執(zhí)行的操作分離開(kāi)來(lái)。例如,它可以將一個(gè)編譯器中的分析邏輯和代碼生成邏輯分離開(kāi)來(lái)。有了這樣的分離,想使用不同的代碼生成器就會(huì)很輕易。更大的好處還有,其它一些公用程序,如lint,可以在使用分析邏輯的同時(shí)免受代碼生成邏輯之累。不幸的是,向集合中增加新的對(duì)象往往需要修改已經(jīng)寫(xiě)好的Visitor類。本文提出了一種在java中實(shí)現(xiàn)Visitor模式的更靈活的方法:使用Reflection(反射)。
  
  集合(Collection)普遍應(yīng)用于面向?qū)ο缶幊讨校步?jīng)常引發(fā)一些和代碼有關(guān)的疑問(wèn)。例如,"假如一個(gè)集合存在不同的對(duì)象,該如何對(duì)它執(zhí)行操作?"
  
  一種方法是,對(duì)集合中的每個(gè)元素進(jìn)行迭代,然后基于所在的類,對(duì)每個(gè)元素分別執(zhí)行對(duì)應(yīng)的操作。這會(huì)很難辦,非凡是,假如你不知道集合中有什么類型的對(duì)象。例如,假設(shè)想打印集合中的元素,你可以寫(xiě)出如下的一個(gè)方法(method):
  
  public void messyPRintCollection(Collection collection) {
  Iterator iterator = collection.iterator()
  while (iterator.hasNext())
   System.out.println(iterator.next().toString())
  }
  
  這看起來(lái)夠簡(jiǎn)單的了。它只不過(guò)調(diào)用了Object.toString()方法,然后打印出對(duì)象,對(duì)嗎?但假如有一組哈希表怎么辦?事情就會(huì)開(kāi)始變得復(fù)雜起來(lái)。你必須檢查從集合中返回的對(duì)象的類型:
  
  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());
  }
  }
  
  不錯(cuò),現(xiàn)在已經(jīng)解決了嵌套集合的問(wèn)題,但它需要對(duì)象返回String,假如有其它不返回String的對(duì)象存在怎么辦?假如想在String對(duì)象前后添加引號(hào)以及在Float后添加f又該怎么辦?代碼還是越來(lái)越復(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ù)雜度會(huì)急劇增長(zhǎng)。你當(dāng)然不想讓一段代碼到處充斥著if-else語(yǔ)句!那怎么避免呢?Visitor模式可以幫你。
  
  要實(shí)現(xiàn)Visitor模式,得為訪問(wèn)者建立一個(gè)Visitor接口,還要為被訪問(wèn)的集合建立一個(gè)Visitable接口。然后,讓具體類實(shí)現(xiàn)Visitor和Visitable接口。這兩個(gè)接口如下所示:
  
  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);
  }
  
  對(duì)于具體的String,可能是這樣:
  
  public class VisitableString implements Visitable
  {
  private String value;
  public VisitableString(String string) {
  value = string;
  }
  public void accept(Visitor visitor) {
   visitor.visitString(this);
  }
  }
  
  在accept方法中,對(duì)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類的時(shí)候,它們也是各自調(diào)用合適的Visitor方法,所得到的效果和前面那個(gè)用了if-else的messyPrintCollection方法一樣,但這里的手法更干凈。在visitCollection()中,調(diào)用的是Visitable.accept(this),然后這個(gè)調(diào)用又返回去調(diào)用一個(gè)合適的Visitor方法。這被稱做 "雙分派";即,Visitor先調(diào)用了Visitable類中的方法,這個(gè)方法又回調(diào)到Visitor類中。
  
  雖然通過(guò)實(shí)現(xiàn)visitor消除了if-else語(yǔ)句,卻也增加了很多額外的代碼。最初的String和Float對(duì)象都要用實(shí)現(xiàn)了Visitable接口的對(duì)象進(jìn)行包裝。這有點(diǎn)討厭,但一般說(shuō)來(lái)不是問(wèn)題,因?yàn)槟憧梢宰尳?jīng)常被訪問(wèn)的集合只包含那些實(shí)現(xiàn)了Visitable接口的對(duì)象。
  
  但似乎這還是額外的工作。更糟糕的是,當(dāng)增加一個(gè)新的Visitable類型如VisitableInteger時(shí),會(huì)發(fā)生什么呢?這是Visitor模式的一個(gè)重大缺陷。假如想增加一個(gè)新的Visitable對(duì)象,就必須修改Visitor接口,然后對(duì)每一個(gè)Visitor實(shí)現(xiàn)類中的相應(yīng)的方法一一實(shí)現(xiàn)。你可以用一個(gè)帶缺省空操作的Visitor抽象基類來(lái)代替接口。那就很象Java GUI中的Adapter類。那個(gè)方法的問(wèn)題在于,它需要占用單繼續(xù);而你往往想保留單繼續(xù),讓它用于其它什么東西,比如繼續(xù)StringWriter。那個(gè)方法還有限制,它只能夠成功訪問(wèn)Visitable對(duì)象。
  
  幸運(yùn)的是,Java可以讓Visitor模式更靈活,使得你可以隨心所欲地增加Visitable對(duì)象。怎么做?答案是,使用Reflection。比如,可以設(shè)計(jì)這樣一個(gè)ReflectiveVisitor接口,它只需要一個(gè)方法:
  
  public interface ReflectiveVisitor {
  public void visit(Object o);
  }
  
  就這樣,很簡(jiǎn)單。至于Visitable,還是和前面一樣,我過(guò)一會(huì)兒再說(shuō)。現(xiàn)在先用Reflection來(lái)實(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(),請(qǐng)求就會(huì)分發(fā)到正確的方法上。很不錯(cuò)的一點(diǎn)是,只要認(rèn)為適合,visit()就可以分發(fā)。這并非必須使用reflection--它可以使用其它完全不同的機(jī)制。
  
  新的PrintVisitor中,有針對(duì)Collection,String和Float而寫(xiě)的方法,但然后它又在catch語(yǔ)句中捕捉所有未處理的類型。你要擴(kuò)展visit()方法,使得它也能夠處理所有的父類。首先,得增加一個(gè)新方法,稱為getMethod(Class c),它返回的是要調(diào)用的方法;為了找到這個(gè)相匹配的方法,先在類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 = "vis

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 西和县| 八宿县| 登封市| 汉川市| 凤庆县| 封丘县| 南昌县| 平武县| 屯留县| 永年县| 寻甸| 冷水江市| 行唐县| 基隆市| 新竹市| 南华县| 固阳县| 禄丰县| 黄骅市| 定远县| 铜梁县| 门头沟区| 长宁县| 台安县| 望江县| 开封市| 米易县| 即墨市| 阳东县| 夏河县| 普安县| 开远市| 濉溪县| 旌德县| 扬中市| 精河县| 徐汇区| 通海县| 广州市| 兖州市| 平安县|