惠阳网站推广费用,wordpress首页文章标签,wordpress链接速度慢,微网站后台录入设计模式--行为型--访问者模式 访问者模式定义结构案例优缺点使用场景扩展分派动态分派静态分派双分派 访问者模式
定义
封装一些作用于某种数据结构中的各元素的操作#xff0c;它可以在不改变这个数据结构的前提下定义作用于这些元素的新操作。
结构
抽象访问者角色它可以在不改变这个数据结构的前提下定义作用于这些元素的新操作。
结构
抽象访问者角色Visitor定义了对每一个元素访问的行为它的参数就是可以访问的元素它的方法个数理论上来说于元素类个数是一样的从这点上来看访问者模式要求元素类的个数不能改变。具体访问者角色Concrete Visitor给出对每一个元素类访问时所产生的具体行为。抽象元素角色Element定义了一个接受访问者的方法其意义是指每一个元素都要可以被访问者访问。具体元素角色Concrete Element提供接受访问方法的具体实现而这个具体的实现通常情况下是使用访问者提供的访问该元素类的方法。对象结构角色Object Structure定义当中所提到的对象结构对象机构是一个抽象表述具体点可以理解为一个具有容器性质或者复合对象特性的类它会含有一组元素并且可以迭代这些元素供访问者访问。
案例
给宠物喂食
访问者角色给宠物喂食的人具体访问者角色主人其他人抽象元素角色动物抽象类具体元素角色宠物狗宠物猫结构对象角色主人家 类图
/*** 抽象元素角色类*/
public interface Animal {// 接受访问者访问的功能void accept(Person person);
}/*** 具体元素角色类 猫*/
public class Cat implements Animal{Overridepublic void accept(Person person) {person.feed(this);System.out.println(喵喵喵~);}
}/*** 具体元素角色类 狗*/
public class Dog implements Animal{Overridepublic void accept(Person person) {person.feed(this);System.out.println(汪汪汪~);}
}/*** 抽象访问者角色类*/
public interface Person {void feed(Cat cat);void feed(Dog dog);
}/*** 具体访问者角色类 自己*/
public class Owner implements Person{Overridepublic void feed(Cat cat) {System.out.println(主人喂猫);}Overridepublic void feed(Dog dog) {System.out.println(主人喂狗);}
}/*** 具体访问者角色类 别人*/
public class Someone implements Person{Overridepublic void feed(Cat cat) {System.out.println(别人喂猫);}Overridepublic void feed(Dog dog) {System.out.println(别人喂狗);}
}/*** 对象结构类*/
public class Home {// 声明一个集合对象用来存储元素对象private ListAnimal nodeList new ArrayList();// 添加元素public void add(Animal animal){nodeList.add(animal);}public void action(Person person){// 遍历集合获取每一个元素让访问者访问每一个元素for (Animal animal : nodeList) {animal.accept(person);}}
}public class Test01 {public static void main(String[] args) {// 创建home对象Home home new Home();// 添加元素home.add(new Cat());home.add(new Dog());// 创建主人对象Owner owner new Owner();// 让主人喂所有的宠物home.action(owner);}
}优缺点
优点 扩展性好在不修改对象结构中元素的情况下为对象结构中的元素添加新的功能。复用性好通过访问者来定义整个对象结构通用的功能从而提高复用程度。分离无关行为通过访问者来分离无关的行为把相关的行为封装在一起构成一个访问者这样每一个访问者的功能都比较单一。 缺点 对象结构变化困难在访问者模式中每增加一个新的元素类都要在每一个具体访问者类中增加相应的具体操作违背了开闭原则违反了依赖倒置原则访问者模式依赖了具体类而没有依赖抽象类。
使用场景
对象结构相对稳定但操作算法经常变化对象结构中的对象需要提供多种不同且不相关的操作而且要避免让这些操作的变化影响对象的结构。
扩展
访问者用到了一种双分派技术。
分派
变量被声明时的类型叫做变量的静态类型又称明显类型而变量所引起的对象的真实类型又叫做变量的实际类型。比如Map map new HashMap()map变量的静态类型是Map实际类型是HashMap。根据对象的类型而对方法进行的选择。就是分派Dispatch分派又分两种静态分派和动态分派。
静态分派发生在编译时期分派根据静态类型信息发生。方法重载就是静态分派。动态分派发生在运行时期动态分派动态的置换掉某个方法Java就是通过方法的重写支持动态分派。
动态分派
通过方法的重写支持动态分派
public class Animal {public void execute(){System.out.println(animal);}
}public class Dog extends Animal{Overridepublic void execute() {System.out.println(dog);}
}public class Cat extends Animal{Overridepublic void execute() {System.out.println(cat);}
}public class Test{public static void main(String[] args) {Animal animal new Dog();animal.execute();Animal animal1 new Cat();animal1.execute();}
}上面是多态运行执行的是子类中的方法。 Java编译器在编译时期并不总是知道哪些代码会被执行因为编译器仅仅知道对象的静态类型而不知道对象的真是类型而方法的调用则是根据对象的真实类型而不是静态类型。
静态分派
通过方法重载支持静态分派
public class Animal {public void execute(){System.out.println(animal);}
}public class Dog extends Animal{
}public class Cat extends Animal{
}public class Execute{public void execute(Animal animal){System.out.println(animal);}public void execute(Cat cat){System.out.println(cat);}public void execute(Dog dog){System.out.println(dog);}
}public class Test{public static void main(String[] args) {Animal animal new Animal();Animal animal2 new Cat();Animal animal3 new Dog();Execute execute new Execute();execute.execute(animal); // animalexecute.execute(animal2); // animalexecute.execute(animal3); // animal}
}重载方法的分派是根据静态类型进行的这个分派过程在编译时期就完成了。
双分派
在选择一个方法的时候不仅仅要根据消息接收者的运行时区别还要根据参数的运行时区别
public class Animal {public void accept(Execute execute){execute.execute(this);}
}public class Dog extends Animal{public void accept(Execute execute) {execute.execute(this);}
}public class Cat extends Animal{public void accept(Execute execute) {execute.execute(this);}
}public class Execute{public void execute(Animal animal){System.out.println(animal);}public void execute(Cat cat){System.out.println(cat);}public void execute(Dog dog){System.out.println(dog);}
}public class Test{public static void main(String[] args) {Animal animal new Animal();Animal animal2 new Cat();Animal animal3 new Dog();Execute execute new Execute();animal.accept(execute); // animalanimal2.accept(execute); // catanimal3.accept(execute); // dog}
}上面代码中客户端将Execute对象作为参数传递给Animal类型的变量调用的方法这里完成第一次分派这里是方法重写所以是动态分派也就是执行实际类型中的方法同时也是将自己this作为参数传递进去这里就完成了第二次分派这里的Execute类中有多个重载的方法而传递进行的是this就是具体的实际类型的对象。 双分派实现动态绑定的本质就是在重载方法委派的前面加上了继承体系中覆盖的环节由于覆盖是动态的所以重载就是动态的了。