快捷搜索:

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

一,造访者模式的角色:

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

详细造访者:实现抽象造访者所声明的接口

抽象节点:声明一个吸收操作,吸收一个造访者工具作为参量

详细节点:实现了抽象元素所规定的吸收操作

布局工具:遍历布局中的所有元素,类似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都是动态单分派,静态多分派说话

多分派的说话有:CLOSCecil

造访差异类型的聚拢类--visitor模式入门

造访差异类型的聚拢类--visitor模式入门

本文对应代码下载这里

一,问题提出

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

1Iterator ie=list.iterator();

2while (ie.hasNext()){

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

4p.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());

....

二:办理

要求能造访到每个类的对应的措施。我们第一反映应该是这样的。

1Iterator ie=list.iterator();

2while (ie.hasNext()){

3Object obj=ie.next();

4if(objinstanceofPA){

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

6} elseif (objinstanceofPB){

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

8} elseif (objinstanceofPC){

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

10} elseif (objinstanceofPD){

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

12} elseif (objinstanceofPE){

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

14}

15 }

三:新问题及阐发办理

当数目变多的时刻,掩护if else是个辛勤气的工作:

仔细阐发if,else做的事情,首先判断类型,然後根据类型履行响应的函数

若何才能办理这两个问题呢?首先想到的是java的多态,多态便是根据参数履行响应的内容,

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

1publicclassvisitor{

2publicvoidrun(PA pa){

3pa.runPA();

4}

5publicvoidrun(PB pb){

6pb.runPB();

7}

8publicvoidrun(PC pc){

9pc.runPC();

10}

11publicvoidrun(PD pd){

12pd.runPD();

13}

14publicvoidrun(PE pe){

15pe.runPE();

16}

17 }

这样只要调用run措施,传入对应的参数就能履行了。

还有一个问题便是判断类型。因为重载(overloading)是静态多分配(java说话本身是支持"静态多分配"的。

关于这个观点请看这里)以是造成重载只根据传入工具的定义类型,而不是实际的类型,以是必须在传入前就确定类型,

这可是个难的问题,由于在容器中工具全是Object,出来后如果判断是什么类型必须用

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

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

那若何使用这个来实现呢?看下边这个措施:

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

1publicvoidaccept(visitor v){

2// 把自己传入1

3v.run( this );

4 }

5 然後在visitor中加入一个措施

6publicvoidrun(P p){

7// 把自己传入2

8p.accept( this );

9 }

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

11Visitor v=newVisitor();

12 Iterator ie=list.iterator();

13while (ie.hasNext()){

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

15p.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()等等。。。

这个时刻按照名单服务才是最好的法子。

您可能还会对下面的文章感兴趣: