分享
 
 
 

visitor模式概念——visitor模式进一步

王朝other·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

一,访问者模式的角色:

抽象访问者:声明一个或者多个访问操作,形成所有的具体元素都要实现的接口

具体访问者:实现抽象访问者所声明的接口

抽象节点:声明一个接受操作,接受一个访问者对象作为参量

具体节点:实现了抽象元素所规定的接受操作

结构对象:遍历结构中的所有元素,类似List Set等

二,在什么情况下应当使用访问者模式

访问者模式应该用在被访问类结构比较稳定的时候,换言之系统很少出现增加新节点的

情况。因为访问者模式对开-闭原则的支持并不好,访问者模式答应在节点中加入方法,

是倾斜的开闭原则,类似抽象工厂。

三,访问者模式的缺点:

1,增加节点困难

2,破坏了封装

因为访问者模式的缺点和复杂性,很多设计师反对使用访问者模式。个人感觉应该在了解的

情况下考虑衡量选择.

静态分派,动态分派,多分派,单分派 -------------- visitor模式预备

一,静态分派:

1,定义:发生在编译时期,分派根据静态类型信息发生,重载就是静态分派

2,什么是静态类型:变量被声明时的类型是静态类型

什么是动态类型:变量所引用的对象的真实类型

3,有两个类,BlackCat ,WhiteCat都继续自Cat

如下调用

class Cat{}

class WhiteCat extends Cat{}

class BlackCat extends Cat{}

public class Person {

public void feed(Cat cat){

System.out.println("feed cat");

}

public void feed(WhiteCat cat){

System.out.println("feed WhiteCat");

}

public void feed(BlackCat cat){

System.out.println("feed BlackCat");

}

public static void main(String[] args) {

Cat wc = new WhiteCat();

Cat bc = new BlackCat();

Person p = new Person();

p.feed(wc);

p.feed(bc);

}

}

运行结果是:

feed cat

feed cat

这样的结果是因为重载是静态分派,在编译器执行的,取决于变量的声明类型,因为wc ,bc都是Cat所以调用的都是feed(Cat cat)的函数.

二,动态分派

1,定义:发生在运行期,动态分派,动态的置换掉某个方法。

还是上边类似的例子:

class Cat{

public void eat(){

System.out.println("cat eat");

}

}

public class BlackCat extends Cat{

public void eat(){

System.out.println("black cat eat");

}

public static void main(String[] args){

Cat cat = new BlackCat();

cat.eat();

}

}这个时候的结果是:

black cat eat

这样的结果是因为在执行期发生了向下转型,就是动态分派了。

三,单分派:

1,定义:根据一个宗量的类型进行方法的选择

四,多分派:

1,定义:根据多于一个宗量的类型对方法的选择

2,说明:多分派其实是一系列的单分派组成的,区别的地方就是这些但分派不能分割。

3,C++ ,Java都是动态单分派,静态多分派语言

多分派的语言有:CLOS Cecil

访问差异类型的集合类--visitor模式入门

访问差异类型的集合类--visitor模式入门

本文对应代码下载这里

一,问题提出

访问同一类型的集合类是我们最常见的事情了,我们工作中这样的代码太常见了。

1 Iterator ie = list.iterator();

2 while (ie.hasNext()) {

3 Person p = (Person)ie.next();

4 p.doWork();

5 }

这种访问的特点是集合类中的对象是同一类对象Person,他们拥有功能的方法run,我们调用的恰好是这个共同的方法。

在大部份的情况下,这个是可以的,但在一些复杂的情况,如被访问者的继续结构复杂,被访问者的并不是同一类对象,

也就是说不是继续自同一个根类。方法名也并不相同。例如Java GUI中的事件就是一个例子。

例如这样的问题,有如下类和方法:

类:PA ,方法:runPA();

类:PB ,方法:runPB();

类:PC ,方法:runPC();

类:PD ,方法:runPD();

类:PE ,方法:runPE();

有一个集合类List

List list = new ArrayList();

list.add(new PA());

list.add(new PB());

list.add(new PC());

list.add(new PD());

list.add(new PE());

....

二:解决

要求能访问到每个类的对应的方法。我们第一反应应该是这样的。

1 Iterator ie = list.iterator();

2 while (ie.hasNext()) {

3 Object obj = ie.next();

4 if (obj instanceof PA) {

5 ((PA)obj).runPA();

6 } else if (obj instanceof PB) {

7 ((PB)obj).runPB();

8 } else if (obj instanceof PC) {

9 ((PC)obj).runPC();

10 } else if (obj instanceof PD) {

11 ((PD)obj).runPD();

12 } else if (obj instanceof PE) {

13 ((PE)obj).runPE();

14 }

15 }

三:新问题及分析解决

当数目变多的时候,维护if else是个费力气的事情:

仔细分析if,else做的工作,首先判定类型,然後根据类型执行相应的函数

如何才能解决这两个问题呢?首先想到的是java的多态,多态就是根据参数执行相应的内容,

能很轻易的解决第二个问题,我们可以写这样一个类:

1 public class visitor {

2 public void run(PA pa) {

3 pa.runPA();

4 }

5 public void run(PB pb) {

6 pb.runPB();

7 }

8 public void run(PC pc) {

9 pc.runPC();

10 }

11 public void run(PD pd) {

12 pd.runPD();

13 }

14 public void run(PE pe) {

15 pe.runPE();

16 }

17 }

这样只要调用run方法,传入对应的参数就能执行了。

还有一个问题就是判定类型。由于重载(overloading)是静态多分配(java语言本身是支持"静态多分配"的。

关于这个概念请看这里)所以造成重载只根据传入对象的定义类型,而不是实际的类型,所以必须在传入前就确定类型,

这可是个难的问题,因为在容器中对象全是Object,出来后要是判定是什么类型必须用

if (xx instanceof xxx)这种方法,假如用这种方法启不是又回到了原点,有没有什么更好的办法呢?

我们知到Java还有另外一个特点,覆写(overriding),而覆写是"动态单分配"的(关于这个概念见这里),

那如何利用这个来实现呢?看下边这个方法:

我们让上边的一些类PA PB PC PD PE都实现一个接口P,加入一个方法,accept();

1 public void accept(visitor v) {

2 // 把自己传入1

3 v.run( this );

4 }

5 然後在visitor中加入一个方法

6 public void run(P p) {

7 // 把自己传入2

8 p.accept( this );

9 }

10 // 这样你在遍历中可以这样写

11 Visitor v = new Visitor();

12 Iterator ie = list.iterator();

13 while (ie.hasNext()) {

14 P p = (P)ie.next();

15 p.accept(v);

16 }

17 }

首先执行的是"把自己传入2",在这里由于Java的特性,实际执行的是子类的accept(),也就是实际类的accept

然後是"把自己传入1",在这里再次把this传入,就明确类型,ok我们巧妙的利用overriding解决了这个问题

其实归纳一下第二部分,一个要害点是"自己熟悉自己",是不是很可笑。

其实在计算计技术领域的很多技术上看起来很高深的东西,其实就是现有社会中人的生活方式的一种映射

而且这种方式是简单的不能再简单的方式。上边的全部过程基本上是一个简单的visitor模式实现,visitor模式

已经是设计模式中比较复杂的模式了,但其实原理简单到你想笑。看看下边这个比喻也许你的理解会更深刻。

四:一个帮助理解的比喻:

题目:指挥工人工作

条件:你有10个全能工人,10样相同工作。

需求:做完工作

实现:大喊一声所有人去工作

条件变了,工人不是全能,但是工作相同,ok问题不大

条件再变,工作不是相同,但工人是全能,ok问题不大

以上三种情况在现实生活中是很少发生得,最多的情况是这样:

10个工人,每人会做一种工作,10样工作。你又一份名单Collection)写着谁做什么。但你不熟悉任何人

这个时候你怎么指挥呢,方案一:

你可以一个个的叫工人,然後问他们名字,熟悉他们,查名单,告诉他们做什么工作。

你可以直接叫出他们名字,告诉他们干什么,不需要知到他是谁。

看起来很简单。但假如你要指挥10万人呢 ?而且人员是流动的,天天的人不同,你天天拿到一张文档。

其实很简单,最常用的做法是,你把这份名单贴在墙上,然後大喊一声,所有人按照去看,按照自己的分配情况去做。

这里利用的要害点是"所有工人自己熟悉自己",你不能苛求每个工人会做所有工作,不能苛求所有工作相同,但你

能要求所有工人都熟悉自己。

再想想我们开始的程序,每个工人对应着PA PB PC PD PE....

所有的工人都使工人P

每个工人会做的东西不一样runPA runPB runPC

你有一份名单Visitor(重载)记录着谁做什么工作。

看完上边这些,你是不是会产生如下的问题:

问题:为什么不把这些方法的方法名做成一样的,那就可以解决了。

例如,我们每个PA ,PB ,PC都加入一个run 方法,然後run内部再调用自己对应的runPx()方法。

答案:有些时候从不同的角度考虑,或者因为实现的复杂度早成很难统一方法名。

例如上边指挥人工作的例子的例子,其实run方法就是大叫一声去工作,因为每个工人只会做一种工作,所以能行

但我们不能要求所有人只能会做一种事情,这个要求很愚蠢。所以假如每个工人会干两种或者多种工作呢,

也就是我PA 有runPA() walkPA()等等方法, PB有runPB() climbPB()等等。。。

这个时候按照名单做事才是最好的办法。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有