- 封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作,它可以在不改變這個(gè)數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新操作摆霉。
結(jié)構(gòu)
- 抽象訪問者(Visitor)角色:定義了對(duì)每一個(gè)元素(Element)訪問的行為群嗤,它的參數(shù)就是可以訪問的元素,它的方法個(gè)數(shù)理論上來講與元素類個(gè)數(shù)(Element的實(shí)現(xiàn)類個(gè)數(shù))是一樣的获黔,從這點(diǎn)不難看出痹雅,訪問者模式要求元素類的個(gè)數(shù)不能改變仰担。
- 具體訪問者(ConcreteVisitor)角色:給出對(duì)每一個(gè)元素類訪問時(shí)所產(chǎn)生的具體行為。
- 抽象元素(Element)角色:定義了一個(gè)接受訪問者的方法(accept)绩社,其意義是指摔蓝,每一個(gè)元素都要可以被訪問者訪問。
- 具體元素(ConcreteElement)角色: 提供接受訪問方法的具體實(shí)現(xiàn)愉耙,而這個(gè)具體的實(shí)現(xiàn)贮尉,通常情況下是使用訪問者提供的訪問該元素類的方法。
- 對(duì)象結(jié)構(gòu)(Object Structure)角色:定義當(dāng)中所提到的對(duì)象結(jié)構(gòu)朴沿,對(duì)象結(jié)構(gòu)是一個(gè)抽象表述绘盟,具體點(diǎn)可以理解為一個(gè)具有容器性質(zhì)或者復(fù)合對(duì)象特性的類,它會(huì)含有一組元素(Element)悯仙,并且可以迭代這些元素,供訪問者訪問吠卷。
實(shí)例
// 抽象元素角色(被訪問者)
// Animal.java
public interface Animal {
// 接受訪問者訪問的功能
void accept(Person person);
}
// 具體元素角色
// Cat.java
public class Cat implements Animal{
@Override
public void accept(Person person) {
person.feed(this); // 訪問者給貓喂食
System.out.println("貓吃飽了");
}
}
// 具體元素角色
// Dog.java
public class Dogimplements Animal{
@Override
public void accept(Person person) {
person.feed(this); // 訪問者給狗喂食
System.out.println("狗吃飽了");
}
}
// 抽象訪問者角色
// Person.java
public interface Person {
// 喂食狗
void feed(Cat cat);
// 喂食貓
void feed(Dog dog);
}
// 具體訪問者角色
// Owner.java
public class Owner implements Person{
@Override
public void feed(Cat cat) {
System.out.println("主人喂食貓");
}
@Override
public void feed(Dog dog) {
System.out.println("主人喂食狗");
}
}
// 具體訪問者角色
// Someone.java
public class Someone implements Person{
@Override
public void feed(Cat cat) {
System.out.println("客人喂食貓");
}
@Override
public void feed(Dog dog) {
System.out.println("客人喂食狗");
}
}
// 對(duì)象結(jié)構(gòu)角色
// Home.java
public class Home {
// 聲明一個(gè)結(jié)合對(duì)象,用來存儲(chǔ)元素對(duì)象
private List<Animal> nodeList = new ArrayList<>();
// 添加元素功能
public void add(Animal animal){
nodeList.add(animal);
}
public void action(Person person){
// 遍歷結(jié)合,獲取每一個(gè)元素,讓訪問者訪問每一個(gè)元素
for(Animal animal:nodeList){
animal.accept(person);
}
}
}
// Client.java
public class Client {
public static void main(String[] args) {
// 創(chuàng)建home對(duì)象
Home home = new Home();
// 添加元素到Home對(duì)象中
home.add(new Dog());
home.add(new Cat());
// 創(chuàng)建主人對(duì)象
Owner owner = new Owner();
// 讓主人喂食寵物
home.action(owner);
}
}
Java中的分派
- 變量被聲明時(shí)的類型叫做變量的靜態(tài)類型锡垄;
- 變量所引用的對(duì)象的類型叫做對(duì)象的真實(shí)類型,也就是實(shí)際類型祭隔。
- 例如
Map map = new HashMap()
中货岭,map變量的靜態(tài)類型是Map,實(shí)際類型是HashMap;
- 根據(jù)對(duì)象的類型而對(duì)方法進(jìn)行的選擇就是分派(Dispatch),分派又分為兩種:靜態(tài)分派和動(dòng)態(tài)分派疾渴。
- 靜態(tài)分派(Static Dispatch)發(fā)生在編譯時(shí)期千贯,分派根據(jù)靜態(tài)類型信息發(fā)生。靜態(tài)分派對(duì)于我們來說并不陌生搞坝,方法重載就是靜態(tài)分派搔谴;
- 動(dòng)態(tài)分派(Dynamic Dispatch)發(fā)生在運(yùn)行時(shí)期,動(dòng)態(tài)分派動(dòng)態(tài)地置換掉了某個(gè)方法桩撮。Java通過對(duì)方法的重寫支持動(dòng)態(tài)分派敦第。
動(dòng)態(tài)分派
public class Animal {
public void execute() {
System.out.println("Animal");
}
}
public class Dog extends Animal {
@Override
public void execute() {
System.out.println("dog");
}
}
public class Cat extends Animal {
@Override
public void execute() {
System.out.println("cat");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Dog();
a.execute();
Animal a1 = new Cat();
a1.execute();
}
}
靜態(tài)分派
public class Animal {
}
public class Dog extends Animal {
}
public class Cat extends Animal {
}
public class Execute {
public void execute(Animal a) {
System.out.println("Animal");
}
public void execute(Dog d) {
System.out.println("dog");
}
public void execute(Cat c) {
System.out.println("cat");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Animal();
Animal a1 = new Dog();
Animal a2 = new Cat();
Execute exe = new Execute();
exe.execute(a);
exe.execute(a1);
exe.execute(a2);
}
}
/*
執(zhí)行結(jié)果:
animal
animal
animal
*/
- 重載方法的分派是根據(jù)靜態(tài)變量類型進(jìn)行的峰弹,這個(gè)分派過程在編譯階段就已經(jīng)完成。
雙分派技術(shù)
public class Animal {
public void accept(Execute exe) {
exe.execute(this);
}
}
public class Dog extends Animal {
public void accept(Execute exe) {
exe.execute(this);
}
}
public class Cat extends Animal {
public void accept(Execute exe) {
exe.execute(this);
}
}
public class Execute {
public void execute(Animal a) {
System.out.println("animal");
}
public void execute(Dog d) {
System.out.println("dog");
}
public void execute(Cat c) {
System.out.println("cat");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Animal();
Animal d = new Dog();
Animal c = new Cat();
Execute exe = new Execute();
a.accept(exe);
d.accept(exe);
c.accept(exe);
}
}
- 在該段代碼的執(zhí)行邏輯中芜果,發(fā)生了兩次分派行為鞠呈。
- 首先在,a右钾、d蚁吝、c三個(gè)對(duì)象在調(diào)用accept()的時(shí)候,根據(jù)對(duì)于方法的重寫舀射,發(fā)生一次動(dòng)態(tài)分派窘茁,也就是執(zhí)行了對(duì)象的實(shí)際類型中的方法;
- 其中后控,在每個(gè)方法的內(nèi)部庙曙,其調(diào)用了execute()方法,在execute()方法中浩淘,根據(jù)傳遞的this類型的不同捌朴,會(huì)靜態(tài)的分派到重載的方法中,發(fā)生了第二次分派张抄。
- 雙分派的意圖就是在靜態(tài)的重載前面加上了動(dòng)態(tài)的重寫砂蔽,從而實(shí)現(xiàn)了動(dòng)態(tài)的重載。