访问者模式
一、引子
对于系统中一个已经完成的类层次结构,我们已经给它提供了满足需求的接口。但是面对新增加的需求,我们应该怎么做呢?如果这是为数不多的几次变动,而且你不用为了一个需求的调整而将整个类层次结构统统地修改一遍,那么直接在原有类层次结构上修改也许是个不错的主意。
但是往往我们遇到的却是:这样的需求变动也许会不停的发生;更重要的是需求的任何变动可能都要让你将整个类层次结构修改个底朝天。这种类似相同的操作分布在不同的类里面,不是一个好现象,我们要对这个结构重构一下了。
那么,访问者模式也许是你很好的选择(定义一个公共的接口,然后实现它并重构需求变动)。
二、定义与结构
访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。
《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于java中的collection概念了。
以下是访问者模式的组成结构:
1)
访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口(客户端和服务器端通信的桥梁,也就是通过接口访问)。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
2) 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作(客户端)。
3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数(客户端需要接受的方法)。
4) 具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。
5) 对象结构角色(Object
Structure):这是使用访问者模式必备的角色。它要具备以下特征:
(1)能枚举它的元素(注册的服务个数);可以提供一个高层的接口以允许该访问者访问它的元素(接口);可以是一
个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
三、举例
import java.util.*;
import junit.framework.*;
//访问者角色
interface Visitor {
void visit(Gladiolus g);
void visit(Runuculus r);
void visit(Chrysanthemum c);
}
// The Flower hierarchy cannot be changed:
//元素角色
interface Flower {
void accept(Visitor v);
}
//以下三个具体元素角色
class Gladiolus implements Flower {
public void accept(Visitor v) { v.visit(this);}
}
class Runuculus implements Flower {
public void accept(Visitor v) { v.visit(this);}
}
class Chrysanthemum implements Flower {
public void accept(Visitor v) { v.visit(this);}
}
// Add the ability to produce a string:
//实现的具体访问者角色
class StringVal implements Visitor {
String s;
public String toString() { return s; }
public void visit(Gladiolus g) {
s = "Gladiolus";
}
public void visit(Runuculus r) {
s = "Runuculus";
}
public void visit(Chrysanthemum c) {
s = "Chrysanthemum";
}
}
// Add the ability to do "Bee" activities:
//另一个具体访问者角色
class Bee implements Visitor {
public void visit(Gladiolus g) {
System.out.println("Bee and Gladiolus");
}
public void visit(Runuculus r) {
System.out.println("Bee and Runuculus");
}
public void visit(Chrysanthemum c) {
System.out.println("Bee and Chrysanthemum");
}
}
//这是一个对象生成器
//这不是一个完整的对象结构,这里仅仅是模拟对象结构中的元素
class FlowerGenerator {
private static Random rand = new Random();
public static Flower newFlower() {
switch(rand.nextInt(3)) {
default:
case 0: return new Gladiolus();
case 1: return new Runuculus();
case 2: return new Chrysanthemum();
}
}
}
//客户测试程序
public class BeeAndFlowers extends TestCase {
/*
在这里你能看到访问者模式执行的流程:
首先在客户端先获得一个具体的访问者角色
遍历对象结构
对每一个元素调用accept方法,将具体访问者角色传入
这样就完成了整个过程
*/
//对象结构角色在这里才组装上
List flowers = new ArrayList();
public BeeAndFlowers() {
for(int i = 0; i < 10; i++)
flowers.add(FlowerGenerator.newFlower());
}
Visitor sval ;
public void test() {
// It’s almost as if I had a function to
// produce a Flower string representation:
//这个地方你可以修改以便使用另外一个具体访问者角色
sval = new StringVal();
Iterator it = flowers.iterator();
while(it.hasNext()) {
((Flower)it.next()).accept(sval);
System.out.println(sval);
}
}
public static void main(String args[]) {
junit.textui.TestRunner.run(BeeAndFlowers.class);
}
}
四、双重分派
对了,你在上面的例子中体会到双重分派的实现了没有?
首先在客户程序中将具体访问者模式作为参数传递给具体元素角色(加亮的地方所示)。这便完成了一次分派。
进入具体元素角色后,具体元素角色调用作为参数的具体访问者模式中的visitor方法,同时将自己(this)作为参数传递进去。具体访问者模式再
根据参数的不同来选择方法来执行(加亮的地方所示)。这便完成了第二次分派。
五、优缺点及适用情况
先来看下访问者模式的使用能否避免引言中的痛苦。使用了访问者模式以后,对于原来的类层次增加新的操作,仅仅需要实现一个具体访问者角色就可以了,而不
必修改整个类层次。而且这样符合“开闭原则”的要求。而且每个具体的访问者角色都对应于一个相关操作,因此如果一个操作的需求有变,那么仅仅修改一个具体访问者角色,而不用改动整个类层次。
看来访问者模式确实能够解决我们面临的一些问题。
而且由于访问者模式为我们的系统多提供了一层“访问者”,因此我们可以在访问者中添加一些对元素角色的额外操作。
但是“开闭原则”的遵循总是片面的。如果系统中的类层次发生了变化,会对访问者模式产生什么样的影响呢?你必须修改访问者角色和每一个具体访问者角
色……
看来访问者角色不适合具体元素角色经常发生变化的情况。而且访问者角色要执行与元素角色相关的操作,就必须让元素角色将自己内部属性暴露出来,而在
java中就意味着其它的对象也可以访问。这就破坏了元素角色的封装性。而且在访问者模式中,元素与访问者之间能够传递的信息有限,这往往也会限制访问者
模式的使用。
《设计模式》一书中给出了访问者模式适用的情况:
1) 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2)
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。
3) 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
4)
定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常
改变,那么可能还是在这些类中定义这些操作较好。
你是否能很好的理解呢?
六、总结
这是一个巧妙而且复杂的模式,它的使用条件比较苛刻。当系统中存在着固定的数据结构(比如上面的类层次),而有着不同的行为,那么访问者模式也许是个不错的选择。
分享到:
相关推荐
在Android Studio工程中,实现这一模式时,我们通常会在布局XML文件中使用自定义的装饰组件,并在Java代码中通过构造函数传递原始组件,从而实现装饰。例如: ```xml android:id="@+id/animated_button" android...
优化代码的首步、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则、单例模式、Builder模式、...访问者模式、中介者模式、代理模式、组合模式、适配器模式、装饰模式、享元模式、外观模式、桥接模式...
- **行为型模式**包括策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式和解释器模式。这些模式关注于对象间的职责分配。 - **策略模式**:...
在Android开发中,设计模式是提升代码质量和可维护性的重要工具。"android设计模式"这一主题,主要涵盖了一些常见的设计原则、模式以及如何在Android环境中应用它们。《设计模式之禅》作为一本经典著作,其深入浅出...
3. **观察者模式**:BroadcastReceiver是Android中的观察者模式典型应用,它允许组件监听并响应系统或应用程序事件。书中会展示如何实现和使用观察者模式,以及如何处理多线程环境下的事件传递。 4. **代理模式**:...
Android中的LiveData 和 Observer API 就是基于观察者模式,实现数据变化的实时响应。 10. **组合模式(Composite)**:将对象组合成树形结构,以表示“部分-整体”的层次结构。在Android UI布局中, ViewGroup 就是...
在Android中,`BroadcastReceiver`和`LiveData`都是观察者模式的应用。 5. 模板方法模式:定义操作中的算法骨架,而将一些步骤延迟到子类中。`Activity`的生命周期回调方法就是模板方法模式的体现,开发者只需要...
观察者模式是Android中事件处理机制的基础,例如BroadcastReceiver、LiveData等。 ### 实战应用案例 - **单例模式**:在Android项目中,可以利用单例模式来管理全局的网络请求、数据库操作等。 - **适配器模式**:...
在Android应用开发中,Model-View-Presenter(MVP)模式是一种常见的设计模式,它将业务逻辑、数据处理和用户界面进行了分离,提高了代码的可测试性和可维护性。本极简例子旨在帮助开发者理解并掌握如何在Android ...
- **访问者(Visitor)**:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变元素的类的前提下定义作用于这些元素的新操作。 以上设计模式在Android开发中都有广泛的应用,理解并熟练运用它们能帮助...
书中除了介绍这些设计模式的基本概念外,还会详细分析这些模式在Android源码中的具体应用,包括但不限于源码中使用的类、接口和对象创建方式。作者会从源码的角度出发,剖析模式的实际代码实现,帮助读者深入理解...
- 访问者模式:在不改变对象结构的前提下,为对象增加新的操作。 Android SDK源码中广泛使用了这些设计模式,例如,观察者模式在BroadcastReceiver和ContentObserver中体现,单例模式在系统服务的实例化中使用,...
- **访问者模式**:表示一个作用于某对象结构中的各元素的操作。 - **中介者模式**:定义一个中介对象来封装一系列的对象交互。 - **解释器模式**:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这...
- **访问者模式**:表示一个作用于某对象结构中的各元素的操作。 - **中介者模式**:用一个中介对象来封装一系列的对象交互。 - **解释器模式**:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个...
20. **访问者模式**:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。在Android中,较少直接使用,但在某些复杂结构操作中可能出现。 21. **解释器...
- 访问者模式:在不改变对象结构的情况下,增加新的操作。 - 中介者模式:定义一个中介对象来简化原本复杂的对象间交互。 - 解释器模式:为特定语言提供解析结构,常用于编译器和脚本引擎。 4. MVC、MVP和MVVM...
在"Android便签"中,内容提供者负责管理和暴露便签数据,使得其他应用程序也能访问这些数据。开发者需要实现ContentProvider类并覆盖其关键方法,如query()、insert()、update()和delete(),以实现对数据库的操作。 ...
在Java中,`java.util.Observable`和`java.util.Observer`接口就是观察者模式的体现。 4. **装饰器模式**:动态地给一个对象添加一些额外的职责,增加功能而不影响其他对象。在Java IO流中,`BufferedReader`、`...
生产者-消费者模型是计算机科学中的一个经典设计模式,它主要解决的是多线程环境下的资源协调问题。在Android开发中,这个模型被广泛应用来优化性能,提高用户体验,尤其是在处理数据加载、图片加载等异步任务时。...