王朝网络
分享
 
 
 

翻译TIPatterns--复杂的交互(Complex interactions)

王朝other·作者佚名  2006-01-09
宽屏版  字体: |||超大  

复杂的交互(Complex interactions)

多路分派(Multiple dispatching)

处理多种类型之间的交互可能会使程序变的相当杂乱。比如,考虑一个解析和执行数学表达式的系统。你需要支持数字+数字,数字×数字,等等,这里的数字 (Number) 是一系列数字对象的基类。但是,当我们仅仅给出a+b,我们并不知道a或者b的确切类型,那我们又如何让它们正确的交互呢?

问题的答案可能是你所没有想到的:Java只能做单路分派(single dispatching)。也就是说,如果对多余一个的类型未知的对象进行操作,Java只能对这些类型中的一种类型启用动态绑定机制。这并不能解决上面的问题,所以最后你还是得自己手工(写代码)帧测类型并且产生你自己的动态绑定行为。

这个方案就是多路分派(multiple dispatching)。别忘了多态只能通过成员函数调用来实现,所以如果你想实现双路分派(double dispatching),就必须得有两次成员函数调用:第一次调用决定第一个未知类型,第二次调用决定第二个未知类型。对于多路分派,你必须得有一个可供调用的多态方法来决定所有类型。通常,你可以通过设置配置项来实现用一个成员函数调用产生多于一个的动态成员函数调用,从而在这个过程中决定多于一种的类型。为了做到这一点,你需要多于一个的多态方法调用:每一个分派都需要一个调用。下面例子里的(多态)方法是compete()和eval(),它们都是同一个类(型)的成员函数。(这种情况下,只有两次分派,也就是双路分派)。如果你需要处理的是两个体系(hierarchies)的不同类型之间的交互,那每个体系都必须得实现一个多态方法调用。

下面是一个多路分派的例子:(石头剪子布)

//: multipledispatch:PaperScissorsRock.java

// Demonstration of multiple dispatching.

package multipledispatch;

import java.util.*;

import junit.framework.*;

// An enumeration type:

class Outcome {

private String name;

private Outcome(String name) { this.name = name; }

public final static Outcome

WIN = new Outcome("wins"),

LOSE = new Outcome("loses"),

DRAW = new Outcome("draws");

public String toString() { return name; }

}

interface Item {

Outcome compete(Item it);

Outcome eval(Paper p);

Outcome eval(Scissors s);

Outcome eval(Rock r);

}

class Paper implements Item {

public Outcome compete(Item it) { return it.eval(this); }

public Outcome eval(Paper p) { return Outcome.DRAW; }

public Outcome eval(Scissors s) { return Outcome.WIN; }

public Outcome eval(Rock r) { return Outcome.LOSE; }

public String toString() { return "Paper"; }

}

class Scissors implements Item {

public Outcome compete(Item it) { return it.eval(this); }

public Outcome eval(Paper p) { return Outcome.LOSE; }

public Outcome eval(Scissors s) { return Outcome.DRAW; }

public Outcome eval(Rock r) { return Outcome.WIN; }

public String toString() { return "Scissors"; }

}

class Rock implements Item {

public Outcome compete(Item it) { return it.eval(this); }

public Outcome eval(Paper p) { return Outcome.WIN; }

public Outcome eval(Scissors s) { return Outcome.LOSE; }

public Outcome eval(Rock r) { return Outcome.DRAW; }

public String toString() { return "Rock"; }

}

class ItemGenerator {

private static Random rand = new Random();

public static Item newItem() {

switch(rand.nextInt(3)) {

default:

case 0: return new Scissors();

case 1: return new Paper();

case 2: return new Rock();

}

}

}

class Compete {

public static void match(Item a, Item b) {

System.out.println(

a + " " + a.compete(b) + " vs. " + b);

}

}

public class PaperScissorsRock extends TestCase {

static int SIZE = 20;

public void test() {

for(int i = 0; i < SIZE; i++)

Compete.match(ItemGenerator.newItem(),

ItemGenerator.newItem());

}

public static void main(String args[]) {

junit.textui.TestRunner.run(PaperScissorsRock.class);

}

} ///:~

访问者(Visitor),多路分派的一种

假设说你手头有一组早先的类层次体系(class hierachy),这些类都是固定不能改变的;可能它们是从第三方买来的,所以你没法改变这个类层次体系。但是,你可能想给这个类层次体系添加新的多态方法,通常情况下这必须得向基类接口添加新的东西。所以问题就来了:你既需要给基类添加新的方法,而你又不能动基类。那到底该怎么办呢?

解决这类问题的设计模式叫做“访问者(visitor)”(《设计模式》里最后讲到的那个),它是建立在上一节的双路分派机制之上的。

Visitor模式使得你可以通过创建另外一个独立的visitor类型的类层次体系来扩展原始类型的接口,从而仿真(virtualize)实现原本针对原始类型的操作。原始类型的对象只是简单的“接受”访问者,然后调用访问者动态绑定的成员函数。

//: visitor:BeeAndFlowers.java

// Demonstration of "visitor" pattern.

package visitor;

import java.util.*;

import junit.framework.*;

interface Visitor {

void visit(Gladiolus g);

void visit(Runuculus r);

void visit(Chrysanthemum c);

}

// The Flower hierarchy cannot be changed:

interface Flower {

void accept(Visitor v);

}

class Gladiolus implements Flower {

public void accept(Visitor v) { v.visit(this);}

}

class Runuculus implements Flower {

public void accept(Visitor v) { v.visit(this);}

}

class Chrysanthemum implements Flower {

public void accept(Visitor v) { v.visit(this);}

}

// Add the ability to produce a string:

class StringVal implements Visitor {

String s;

public String toString() { return s; }

public void visit(Gladiolus g) {

s = "Gladiolus";

}

public void visit(Runuculus r) {

s = "Runuculus";

}

public void visit(Chrysanthemum c) {

s = "Chrysanthemum";

}

}

// Add the ability to do "Bee" activities:

class Bee implements Visitor {

public void visit(Gladiolus g) {

System.out.println("Bee and Gladiolus");

}

public void visit(Runuculus r) {

System.out.println("Bee and Runuculus");

}

public void visit(Chrysanthemum c) {

System.out.println("Bee and Chrysanthemum");

}

}

class FlowerGenerator {

private static Random rand = new Random();

public static Flower newFlower() {

switch(rand.nextInt(3)) {

default:

case 0: return new Gladiolus();

case 1: return new Runuculus();

case 2: return new Chrysanthemum();

}

}

}

public class BeeAndFlowers extends TestCase {

List flowers = new ArrayList();

public BeeAndFlowers() {

for(int i = 0; i < 10; i++)

flowers.add(FlowerGenerator.newFlower());

}

public void test() {

// It's almost as if I had a function to

// produce a Flower string representation:

StringVal sval = new StringVal();

Iterator it = flowers.iterator();

while(it.hasNext()) {

((Flower)it.next()).accept(sval);

System.out.println(sval);

}

// Perform "Bee" operation on all Flowers:

Bee bee = new Bee();

it = flowers.iterator();

while(it.hasNext())

((Flower)it.next()).accept(bee);

}

public static void main(String args[]) {

junit.textui.TestRunner.run(BeeAndFlowers.class);

}

} ///:~

练习

1. 写一个商务建模系统,它包括三种类型的Inhabitant:侏儒(代表工程师),精灵(代表市场人员),巨人(代表经理)。写一个Project类,创建不同的inhabitants并且让他们交互,必须要用到多路分派。

2. 改写练习1,使他们的交互更详细。每一种Inhabitant都可以用getWeapon()随机的产生一个武器:侏儒用Jargon或者Play,精灵用InventFeature或者SellImaginaryProduct,巨人用Edioct和Schedule。你必须决定在每次交互中哪个武器赢那个武器输(就像PaperScissorsRock.java那样)。给Project类添加一个battle () 成员函数,它接受两种Inhabitants(作为参数),使它们俩火并。再给Project类添加一个meeting()成员函数,创建一组侏儒,精灵和巨人,并让各个组之间开战直到最后剩下一组获胜。

3. 改写PaperScissorsrock.java,用表查询的方法代替双路分派。最简单的做法使写一个值为Maps的Map,用每个对象的类名称来作为Map的键。然后通过下面这种方法查找:

((Map)map.get(o1.getClass())).get(o2.getClass())

使用这种方法非常容易重心配置系统。什么时候使用这种方法会比硬编码的动态分发更合适呢?你能否用表查找的方法写一个语法简单的系统来实现动态分派?

4. 用练习3那种表查找的方法重做练习2。

目录

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有