实现双重派遣

记住多形性只能通过方法调用才能表现出来,所以假如想使双重派遣正确进行,必须执行两个方法调用:

在每种结构中都用一个来判断其中的类型。在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;
  }
} ///:~

可以看到,过载的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的集合初化化过程。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索return
, 方法
, 类型
, public
, 一个
, 16
C方法
劳务派遣实习网、实习派遣网、实习生外包派遣、劳务派遣、劳务派遣公司,以便于您获取更多的相关知识。

时间: 2024-11-02 07:47:01

实现双重派遣的相关文章

多重派遣

上述设计方案肯定是令人满意的.系统内新类型的加入涉及添加或修改不同的类,但没有必要在系统内对代码作大范围的改动.除此以外,RTTI并不象它在RecycleA.java里那样被不当地使用.然而,我们仍然有可能更深入一步,以最"纯"的角度来看待RTTI,考虑如何在垃圾分类系统中将它完全消灭. 为达到这个目标,首先必须认识到:对所有与不同类型有特殊关联的活动来说--比如侦测一种垃圾的具体类型,并把它置入适当的垃圾筒里--这些活动都应当通过多形性以及动态绑定加以控制. 以前的例子都是先按类型排

访问器范式

接下来,让我们思考如何将具有完全不同目标的一个设计范式应用到垃圾归类系统. 对这个范式,我们不再关心在系统中加入新型Trash时的优化.事实上,这个范式使新型Trash的添加显得更加复杂.假定我们有一个基本类结构,它是固定不变的:它或许来自另一个开发者或公司,我们无权对那个结构进行任何修改.然而,我们又希望在那个结构里加入新的多形性方法.这意味着我们一般必须在基础类的接口里添加某些东西.因此,我们目前面临的困境是一方面需要向基础类添加方法,另一方面又不能改动基础类.怎样解决这个问题呢? "访问器

聚美优品IPO谎言:49%移动端数据存疑 派遣工六成

4月12日,成立仅四年的美妆电商聚美优品向美国证券交易委员会提交了招股书.过去几年中,有一系列的中国企业成功在美国上市.年内也有京东.微博.途牛等相继提交了招股书.其中,微博已成功登陆纳斯达克. 2014年,堪称中国互联网公司的美股元年.意气风发的中国互联网新贵们,正扎堆排队等着美国纽交所或纳斯达克钟声鸣响的那一刻. 美国机构投资者们,往往对中资概念股爱恨交加.爱的是,高速发展的中国公司,总有源源不断的故事和想象力.比如,中国的facebook,中国的google,中国的亚马逊.恨的是,中资概念

有关“双重检查锁定失效”的说明

原文地址 译者:丁一 双重检查锁定(以下称为DCL)已被广泛当做多线程环境下延迟初始化的一种高效手段. 遗憾的是,在Java中,如果没有额外的同步,它并不可靠.在其它语言中,如c++,实现DCL,需要依赖于处理器的内存模型.编译器实行的重排序以及编译器与同步库之间的交互.由于c++没有对这些做出明确规定,很难说DCL是否有效.可以在c++中使用显式的内存屏障来使DCL生效,但Java中并没有这些屏障. 来看下面的代码 01 // Single threaded version 02 class

双重博客轮链接的介绍以及可行性分析

轮链现在已经被广大的网站优化人员所熟知,有一部分人已经采用了这种新型的SEO技巧来对网站进行优化,在A5上也有很多轮链技术的文章,如果你还不知道轮链或者不知道如何使用轮链的话可以看看A5上其他的文章,本文主要的探讨点并不是对于轮链的介绍以及使用,而是在使用轮链过程中衍生出来的一种新的轮链模式的介绍和探讨. 首先,我个人总结的两点博客轮链的关键点如下: 1.通过外链提高目标网站的权重 2.通过单向链接循环提高博客群的权重. 这两句话看似有点类似,但完全不同,第一点是说明了轮链的目的,第二点则说明了

java程序中双重检查锁定与延迟初始化

在java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化.此时程序员可能会采用延迟初始化.但要正确实现线程安全的延迟初始化需要一些技巧,否则很容易出现问题.比如,下面是非线程安全的延迟初始化对象的示例代码: public class UnsafeLazyInitialization { private static Instance instance; public static Instance getInstance() { if (instanc

单例模式中双重锁定的例子

学习单例模式时,好多人都不太理解双重锁定.学完后突然想到一个很有趣的例子. 单例模式结构图: Singleton类 class Singleton { private static Singleton instance; private static readonly object syncRoot = new object(); //程序运行时创建一个静态只读的进程辅助对象 private Singleton() { } //用private修饰构造方法,防止外界利用new创建此类实例 pub

在预装Vista的PC上安装XP并设双重启动

如今,越来越多的PC在出厂时预装Windows Vista,这在很大程度上给用户带来了方便--虽然预装的Windows Vista多为Home Basic之类简化版本--不过,对许多用户而言,也许更习惯于Windows XP,加上日常使用的某些应用软件可能在Windows Vista中运行不太正常,因此,很多人希望在能系统中安装Windows XP,当然,前提是不影响系统中预装的Windows Vista,毕竟那是花钱买的. 许多用户想知道怎样在预装Windows Vista的PC上安装Wind

[Flash基础理论课16] 矩阵革命[双重循环]

返回"Flash基础理论课 - 目录" 在本次课中我们将介绍两种方法来完成该效果,重点掌握双重循环语句 还要介绍 TextField._alpha 方法不可用的解决办法 单循环: for (i=0; i < 5; i++) { trace("i="+i); } 运行结果如下: i=0 i=1 i=2 i=3 i=4 双重循环: 就是在 for 循环中再加入一个 for 循环 for (i) { for(j) {} } 下面看一个双重循环的例子 for (i=0