实现双重调派
当前位置:以往代写 > JAVA 教程 >实现双重调派
2019-06-14

实现双重调派

实现双重调派

记着多形性只能通过要领挪用才气表示出来,所以如果想使双重调派正确举办,必需执行两个要领挪用:

在每种布局中都用一个来判定个中的范例。在Trash布局中,将利用一个新的要领挪用addToBin(),它回收的参数是由TypeBin组成的一个数组。谁人要领将在数组中遍历,实验将本身插手适当的垃圾筒,这里正是双重调派产生的处所。

实现双重派遣

新成立的分级布局是TypeBin,个中包括了它本身的一个要领,名为add(),并且也应用了多形性。但要留意一个新特点:add()已举办了“过载”处理惩罚,可接管差异的垃圾范例作为参数。因此,双重满意机制的一个要害点是它也要涉及到过载。
措施的从头设计也带来了一个问题:此刻的基本类Trash必需包括一个addToBin()要领。为办理这个问题,一个最直接的步伐是复制所有代码,并修改基本类。然而,如果没有对源码的节制权,那么尚有另一个步伐可以思量:将addToBin()要领置入一个接口内部,保持Trash稳定,并担任新的、非凡的范例Aluminum,Paper,Glass以及Cardboard。我们在这里筹备采纳后一个步伐。
这个设计方案顶用到的大大都类都必需设为public(公用)属性,所以它们安排于本身的类内。下面列出接口代码:

 

//: TypedBinMember.java
// An interface for adding the double dispatching
// method to the trash hierarchy without 
// modifying the original hierarchy.
package c16.doubledispatch;

interface TypedBinMember {
  // The new method:
  boolean addToBin(TypedBin[] tb);
} ///:~

在Aluminum,Paper,Glass以及Cardboard每个特定的子范例内,城市实现接口TypeBinMember的addToBin()要领,但每种环境下利用的代码“好像”都是完全一样的:

 

//: DDAluminum.java
// Aluminum for double dispatching
package c16.doubledispatch;
import c16.trash.*;

public class DDAluminum extends Aluminum 
    implements TypedBinMember {
  public DDAluminum(double wt) { super(wt); }
  public boolean addToBin(TypedBin[] tb) {
    for(int i = 0; i < tb.length; i++)
      if(tb[i].add(this))
        return true;
    return false;
  }
} ///:~
//: DDPaper.java
// Paper for double dispatching
package c16.doubledispatch;
import c16.trash.*;

public class DDPaper extends Paper 
    implements TypedBinMember {
  public DDPaper(double wt) { super(wt); }
  public boolean addToBin(TypedBin[] tb) {
    for(int i = 0; i < tb.length; i++)
      if(tb[i].add(this))
        return true;
    return false;
  }
} ///:~
//: DDGlass.java
// Glass for double dispatching
package c16.doubledispatch;
import c16.trash.*;

public class DDGlass extends Glass 
    implements TypedBinMember {
  public DDGlass(double wt) { super(wt); }
  public boolean addToBin(TypedBin[] tb) {
    for(int i = 0; i < tb.length; i++)
      if(tb[i].add(this))
        return true;
    return false;
  }
} ///:~
//: DDCardboard.java
// Cardboard for double dispatching
package c16.doubledispatch;
import c16.trash.*;

public class DDCardboard extends Cardboard 
    implements TypedBinMember {
  public DDCardboard(double wt) { super(wt); }
  public boolean addToBin(TypedBin[] tb) {
    for(int i = 0; i < tb.length; i++)
      if(tb[i].add(this))
        return true;
    return false;
  }
} ///:~

每个addToBin()内的代码会为数组中的每个TypeBin工具挪用add()。但请留意参数:this。对Trash的每个子类来说,this的范例都是差异的,所以不能认为代码“完全”一样——尽量今后在Java里插手参数化范例机制后便可认为一样。这是双重调派的第一个部门,因为一旦进入这个要领内部,便可知道到底是Aluminum,Paper,照旧其他什么垃圾范例。在对add()的挪用进程中,这种信息是通过this的范例通报的。编译器会阐明出对add()正确的过载版本的挪用。但由于tb[i]会发生指向基本范例TypeBin的一个句柄,所以最终会挪用一个差异的要领——详细什么要领取决于当前选择的TypeBin的范例。那就是第二次调派。
下面是TypeBin的基本类:

 

//: TypedBin.java
// Vector that knows how to grab the right type
package c16.doubledispatch;
import c16.trash.*;
import java.util.*;

public abstract class TypedBin {
  Vector v = new Vector();
  protected boolean addIt(Trash t) {
    v.addElement(t);
    return true;
  }
  public Enumeration elements() {
    return v.elements();
  }
  public boolean add(DDAluminum a) {
    return false;
  }
  public boolean add(DDPaper a) {
    return false;
  }
  public boolean add(DDGlass a) {
    return false;
  }
  public boolean add(DDCardboard a) {
    return false;
  }
} ///:~

#p#分页标题#e#

可以看到,过载的add()要领全城市返回false。假如未在衍生类里对要领举办过载,它就会一直返回false,并且挪用者(今朝是addToBin())会认为当前Trash工具尚未乐成插手一个荟萃,所以会继承查找正确的荟萃。
在TypeBin的每一个子类中,都只有一个过载的要了解被过载——详细取决于筹备建设的是什么垃圾筒范例。举个例子来说,CardboardBin会过载add(DDCardboard)。过载的要了解将垃圾工具插手它的荟萃,并返回true。而CardboardBin中剩余的所有add()要领城市继承返回false,因为它们尚未过载。事实上,如果在这里回收了参数化范例机制,Java代码的自动建设就要利便得多(利用C++的“模板”,我们不必费事地为子类编码,可能将addToBin()要领置入Trash里;Java在这方面另有待改造)。
由于对这个例子来说,垃圾的范例已经定制并置入一个差异的目次,所以需要用一个差异的垃圾数据文件令其运转起来。下面是一个示范性的DDTrash.dat:

 

c16.DoubleDispatch.DDGlass:54
c16.DoubleDispatch.DDPaper:22
c16.DoubleDispatch.DDPaper:11
c16.DoubleDispatch.DDGlass:17
c16.DoubleDispatch.DDAluminum:89
c16.DoubleDispatch.DDPaper:88
c16.DoubleDispatch.DDAluminum:76
c16.DoubleDispatch.DDCardboard:96
c16.DoubleDispatch.DDAluminum:25
c16.DoubleDispatch.DDAluminum:34
c16.DoubleDispatch.DDGlass:11
c16.DoubleDispatch.DDGlass:68
c16.DoubleDispatch.DDGlass:43
c16.DoubleDispatch.DDAluminum:27
c16.DoubleDispatch.DDCardboard:44
c16.DoubleDispatch.DDAluminum:18
c16.DoubleDispatch.DDPaper:91
c16.DoubleDispatch.DDGlass:63
c16.DoubleDispatch.DDGlass:50
c16.DoubleDispatch.DDGlass:80
c16.DoubleDispatch.DDAluminum:81
c16.DoubleDispatch.DDCardboard:12
c16.DoubleDispatch.DDGlass:12
c16.DoubleDispatch.DDGlass:54
c16.DoubleDispatch.DDAluminum:36
c16.DoubleDispatch.DDAluminum:93
c16.DoubleDispatch.DDGlass:93
c16.DoubleDispatch.DDPaper:80
c16.DoubleDispatch.DDGlass:36
c16.DoubleDispatch.DDGlass:12
c16.DoubleDispatch.DDGlass:60
c16.DoubleDispatch.DDPaper:66
c16.DoubleDispatch.DDAluminum:36
c16.DoubleDispatch.DDCardboard:22

下面列出措施剩余的部门:

 

//: DoubleDispatch.java
// Using multiple dispatching to handle more
// than one unknown type during a method call.
package c16.doubledispatch;
import c16.trash.*;
import java.util.*;

class AluminumBin extends TypedBin {
  public boolean add(DDAluminum a) {
    return addIt(a);
  }
}

class PaperBin extends TypedBin {
  public boolean add(DDPaper a) {
    return addIt(a);
  }
}

class GlassBin extends TypedBin {
  public boolean add(DDGlass a) {
    return addIt(a);
  }
}

class CardboardBin extends TypedBin {
  public boolean add(DDCardboard a) {
    return addIt(a);
  }
}

class TrashBinSet {
  private TypedBin[] binSet = {
    new AluminumBin(),
    new PaperBin(),
    new GlassBin(),
    new CardboardBin()
  };
  public void sortIntoBins(Vector bin) {
    Enumeration e = bin.elements();
    while(e.hasMoreElements()) {
      TypedBinMember t = 
        (TypedBinMember)e.nextElement();
      if(!t.addToBin(binSet))
        System.err.println("Couldn't add " + t);
    }
  }
  public TypedBin[] binSet() { return binSet; }
}

public class DoubleDispatch {
  public static void main(String[] args) {
    Vector bin = new Vector();
    TrashBinSet bins = new TrashBinSet();
    // ParseTrash still works, without changes:
    ParseTrash.fillBin("DDTrash.dat", bin);
    // Sort from the master bin into the 
    // individually-typed bins:
    bins.sortIntoBins(bin);
    TypedBin[] tb = bins.binSet();
    // Perform sumValue for each bin...
    for(int i = 0; i < tb.length; i++)
      Trash.sumValue(tb[i].v);
    // ... and for the master bin
    Trash.sumValue(bin);
  }
} ///:~

个中,TrashBinSet封装了各类差异范例的TypeBin,同时尚有sortIntoBins()要领。所有双重调派事件城市在谁人要领里产生。可以看到,一旦配置好布局,再归类成各类TypeBin的事情就变得十分简朴了。除此以外,两个动态要领挪用的效率大概也比其他排序要领高一些。
留意这个系统的利便性主要表此刻main()中,同时还要留意到任何特定的范例信息在main()中都是完全独立的。只与Trash基本类接口通信的其他所有要领都不会受到Trash类中产生的改变的滋扰。
添加新范例需要作出的窜改是完全孤独的:我们伴同addToBin()要领担任Trash的新范例,然后担任一个新的TypeBin(这实际只是一个副本,可以简朴地编辑),最后将一种新范例插手TrashBinSet的荟萃初化化进程。

    关键字:

在线提交作业