面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>>
一、引子
对于系统中一个已经完成的类层次结构,我们已经给它提供了满足需求的接口。但是面对新增加的需求,我们应该怎么做呢?如果这是为数不多的几次变动,而且你不用为了一个需求的调整而将整个类层次结构统统地修改一遍,那么直接在原有类层次结构上修改也许是个不错的主意。
但是往往我们遇到的却是:这样的需求变动也许会不停的发生;更重要的是需求的任何变动可能都要让你将整个类层次结构修改个底朝天……。这种类似的操作分布在不同的类里面,不是一个好现象,我们要对这个结构重构一下了。
那么,访问者模式也许是你很好的选择。
二、定义与结构
访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。
《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的 新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于java中的 collection概念了。
以下是访问者模式的组成结构:
1) 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
2) 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。
3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数。
4) 具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。
5) 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
来张类图就能更加清晰的看清访问者模式的结构了。
那么像引言中假想的。我们应该做些什么才能让访问者模式跑起来呢?首先我们要在原有的类层次结构中添加accept方法。然后将这个类层次中的类放到一个对象结构中去。这样再去创建访问者角色……
三、举例
public interface Person {
void accept(Visitor visitor);
}
public class Woman implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Man implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
访问者接口与实现类,分别代表男人与女人在不同的状态下的表现
public interface Visitor {
public void visit(Man man);
public void visit(Woman girl);
}
//成功时Man与Woman的不同表现
public class Success implements Visitor{
public void visit(Man man) {
System.out.println("当男人成功时,背后多半有一个伟大的女人");
}
public void visit(Woman woman) {
System.out.println("当女人成功时,背后大多有一个不成功的男人");
}
}
//恋爱时Man与Woman的不同表现
public class Love implements Visitor{
public void visit(Man man) {
System.out.println("当男人恋爱时,凡事不懂也装懂");
}
public void visit(Woman girl) {
System.out.println("当女人恋爱时,遇事懂也装不懂");
}
}
ObjectStructure与客户端测试代码
import java.util.*;
public class ObjectStructure {
private List<Person> elements = new ArrayList<Person>();
public void attach(Person element){
elements.add(element);
}
public void detach(Person element){
elements.remove(elements);
}
//遍历各种具体元素并执行他们的accept方法
public void display(Visitor visitor){
for(Person p:elements){
p.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
//实例化具体元素
o.attach(new Man());
o.attach(new Woman());
//当成功时不同元素的不同反映
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
//当恋爱时的不同反映
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
}
}
既定访问者模式的类图:
需求的变化
假设现在需求要扩展数据结构,增加一种具体元素,男与女之外的一种不明物体,我们暂时把它称为“怪兽”,在既有访问者模式的架构下,应该怎样?首先增加一个Bruce类,实现Person接口。最麻烦的是要修改访问者接口及其所有具体访问者!
因为Visit方法中没有包含访问Bruce对象的行为,因此我们被迫要去手工更改Visitor(包括抽象的,具体的),在其中添加有关Bruce对象 的行为,这严重违反了“开放-封闭”原则。究其原因在于目前的结构下,被访问对象与访问对象互相依赖,自然不利于分离变化,必须去掉一层依赖关系。
我们尝试把Visitor对Person(元素)的依赖关系去掉,抽象出对应每个具体元素的ElementVisitor接口 -->ManVisitor,WomanVisitor,然后把Visitor对Person的依赖关系转移到ManVisitor与 WomanVisitor身上。
改造后访问者模式的类图:
现在Visitor接口已经没有任何抽象方法,只是一个空接口,每一个具体元素对应有一个ElementVisitor接口,每一个元素对应的ElementVisitor接口有访问该元素的visit(),相当把原来在Visitor接口中声明工作,交由各个具体ElementVisitor接口完成。
经过改造后的代码:
public interface Visitor {
//退化到没有任何抽象方法
}
新增加ManVisitor,WomanVisitor接口
public interface ManVisitor {
public void visit(Man man);
}
public interface WomanVisitor {
public void visit(Woman w);
}
具体Visitor实现类现在同时实现3个接口
//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Success implements Visitor,WomanVisitor,ManVisitor{
public void visit(Man man) {
System.out.println("当男人成功时,背后多半有一个伟大的女人");
}
public void visit(Woman girl) {
System.out.println("当女人成功时,背后大多有一个不成功的男人");
}
}
//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Love implements Visitor,WomanVisitor,ManVisitor{
public void visit(Man man) {
System.out.println("当男人恋爱时,凡事不懂也装懂");
}
public void visit(Woman girl) {
System.out.println("当女人恋爱时,遇事懂也装不懂");
}
}
Person接口没有变化,依旧只依赖于Visitor接口
- public interface Person {
- void accept(Visitor visitor);
- }
改造后的具体元素类Man与Woman
public class Man implements Person {
// 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了
public void accept(Visitor visitor) {
if (visitor instanceof ManVisitor) {
ManVisitor mv = (ManVisitor) visitor;
mv.visit(this);
}
}
}
public class Woman implements Person {
// 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了
public void accept(Visitor visitor) {
if (visitor instanceof WomanVisitor) {
WomanVisitor wv = (WomanVisitor) visitor;
wv.visit(this);
}
}
}
ObjectStructure与客户端测试代码没有变化
import java.util.*;
public class ObjectStructure {
private List<Person> elements = new ArrayList<Person>();
public void attach(Person element){
elements.add(element);
}
public void detach(Person element){
elements.remove(elements);
}
//遍历各种具体元素并执行他们的accept方法
public void display(Visitor visitor){
for(Person p:elements){
p.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
//实例化具体元素
o.attach(new Man());
o.attach(new Woman());
//当成功时不同元素的不同反映
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
//当恋爱时的不同反映
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
}
}