设计模式:08-访问者模式 / 迭代器模式

@[toc]
16. 访问者模式(Visitor Pattern)
16.1 需求的引入
测评系统的需求:将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不 同的种类,比如 成功、失败 等)
16.2 基本介绍
基本介绍
1) 访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
2) 主要将数据结构与数据操作分离,解决了数据结构和操作耦合性问题。
3) 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。
4) 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作 “污染” 这些对象的类,可以选用访问者模式解决。
对原理类图的说明- 即(访问者模式的角色及职责)
1) Visitor 是抽象访问者,为该对象结构中的 ConcreteElement 的每一个类声明一个 visit 操作
2) ConcreteVisitor :是一个具体的访问值 实现每个有 Visitor 声明的操作,是每个操作实现的部分.
3) ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
4) Element 定义一个 accept 方法,接收一个访问者对象
5) ConcreteElement 为具体元素,实现了 accept 方法
16.3 应用实例
import java.util.ArrayList;
import java.util.List;
/**
* 需求:
* 将人分为男人和女人,对歌手进行测评,当看完某个歌手表演后,
* 得到他们对该歌手不同的评价(评价 有不同 的种类,比如 成功、失败 等),
* 请使用访问者模式来说实现
*
* @author houyu
* @createTime 2020/2/4 20:46
*/
public class Demo {
public static void main(String[] args) {
// 创建 ObjectStructure
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(new Man("张三"));
objectStructure.add(new Man("李四"));
objectStructure.add(new Woman("王五"));
// 成功
Success success = new Success();
objectStructure.display(success);
//
System.out.println("--------------------------------------------");
// 失败
Fail fail = new Fail();
objectStructure.display(fail);
}
/**
* 人(元素Element )
*/
public static abstract class Person {
protected String name;
public Person(String name) {
this.name = name;
}
/** 提供一个方法,让访问者可以访问 */
public abstract void accept(Action action);
}
/**
* 男人(具体元素 ConcreteElement )
*
* 说明
* 1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递 Woman 中(第一次分派)
* 2. 然后 Woman 类调用作为参数的 "具体方法" 中方法 getWomanResult, 同时将自己(this)作为参数传入,完成第二次的分派
*
*/
public static class Man extends Person {
public Man(String name) {
super(name);
}
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
/**
* 女人(具体元素 ConcreteElement )
*/
public static class Woman extends Person {
public Woman(String name) {
super(name);
}
@Override
public void accept(Action action) {
action.getWomanResult(this);
}
}
/**
* 行动(访问者:操作者)
*/
public static abstract class Action {
/** 得到男性 的测评 */
public abstract void getManResult(Man man);
/** 得到女性 的测评 */
public abstract void getWomanResult(Woman woman);
}
/**
* 失败(具体访问者)
*/
public static class Fail extends Action {
@Override
public void getManResult(Man man) {
System.out.println(man.name + "男人给的评价该歌手失败!");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println(woman.name + "女人给的评价该歌手失败!");
}
}
/**
* 成功(具体访问者)
*/
public static class Success extends Action {
@Override
public void getManResult(Man man) {
System.out.println(man.name + ":男人给的评价该歌手成功!");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println(woman.name + ":女人给的评价该歌手成功!");
}
}
/**
* 对象结构: 数据结构,管理很多人(Man , Woman)
*/
public static class ObjectStructure {
private List<Person> personList = new ArrayList<>(16);
public void add(Person person) {
personList.add(person);
}
public void remove(Person person) {
personList.remove(person);
}
/** 显示测评情况 */
public void display(Action action) {
for(Person person : personList) {
person.accept(action);
}
}
}
}
应用案例的小结-双分派
- 上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
- 以上述实例为例,假设我们要添加一个 Wait 的状态类,考察 Man 类和 Woman 类的反应,由于使用了双分派,只需增加一个 Action 子类即可在客户端调用即可,不需要改动任何其他类的代码。
16.4 访问者模式的注意事项和细节
优点
1) 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
2) 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点
1) 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难
2) 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
3) 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.
17. 迭代器模式(Iterator Pattern)
17.1 需求的引入
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。如下:
----计算机学院---
计算机科学与技术
软件工程
网络工程
----信息工程学院------
通信工程
信息工程
17.2 基本介绍
基本介绍
1) 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
2) 如果我们的集合元素是用不同的方式实现的,有数组,还有 java 的集合类,或者还有其他方式,当客户端要遍 历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
3) 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表 示,即:不暴露其内部的结构。
原理类图
对原理类图的说明-即(迭代器模式的角色及职责)
1) Iterator : 迭代器接口,是系统提供,含义 hasNext, next, remove
2) ConcreteIterator : 具体的迭代器类,管理迭代
3) Aggregate :一个统一的聚合接口, 将客户端和具体聚合解耦
4) ConcreteAggreage : 具体的聚合持有对象集合, 并提供一个方法,返回一个迭代器, 该迭代器可以正确遍历 集合
5) Client :客户端, 通过 Iterator 和 Aggregate 依赖子类
17.3 应用实例
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 迭代器模式
*
*
* @author houyu
* @createTime 2020/2/6 10:32
*/
public class Demo {
public static void main(String[] args) {
List<College> list = new ArrayList<>(4);
ComputerCollege computerCollege = new ComputerCollege();
computerCollege.addDepartment("计算机科学与技术");
computerCollege.addDepartment("软件工程");
computerCollege.addDepartment("网络工程");
InfoCollege infoCollege = new InfoCollege();
infoCollege.addDepartment("通信工程");
infoCollege.addDepartment("信息工程");
list.add(computerCollege);
list.add(infoCollege);
OutPutImpl outPut = new OutPutImpl(list);
outPut.print();
/*
* =====计算机学院======
* 计算机科学与技术
* 软件工程
* 网络工程
* =====信息工程学院======
* 通信工程
* 信息工程
*/
}
/**
* 专业
*/
public static class Department {
private String name;
public Department(String name) {
this.name = name;
}
}
/**
* 定义基础集合, 装载系
*/
public static interface College {
String getName();
void addDepartment(String name);
/** 返回一个迭代器,遍历 */
Iterator<Department> createIterator();
}
/**
* 计算机迭代器
*/
public static class ComputerCollegeIterator implements Iterator<Department> {
private Department[] departments;
private int index = 0;
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
return index < departments.length;
}
@Override
public Department next() {
return departments[index++];
}
}
/**
* 计算机集合
*/
public static class ComputerCollege implements College {
private Department[] departments = new Department[3];
private int index = 0;
@Override
public String getName() {
return "计算机学院";
}
@Override
public void addDepartment(String name) {
departments[index++] = new Department(name);
}
@Override
public Iterator<Department> createIterator() {
return new ComputerCollegeIterator(departments);
}
}
/**
* 信息学院迭代器
*/
public static class InfoCollegeIterator implements Iterator<Department> {
private List<Department> departments;
private int index = 0;
public InfoCollegeIterator(List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
return index < departments.size();
}
@Override
public Department next() {
return departments.get(index++);
}
}
/**
* 信息学院集合
*/
public static class InfoCollege implements College {
private List<Department> departments = new ArrayList<>(16);
@Override
public String getName() {
return "信息工程学院";
}
@Override
public void addDepartment(String name) {
departments.add(new Department(name));
}
@Override
public Iterator<Department> createIterator() {
return new InfoCollegeIterator(departments);
}
}
/**
* 打印工具
*/
public static class OutPutImpl {
private List<College> colleges;
public OutPutImpl(List<College> colleges) {
this.colleges = colleges;
}
public void print() {
Iterator<College> iterator = colleges.iterator();
while(iterator.hasNext()) {
College college = iterator.next();
System.out.println("=====" + college.getName() + "======");
Iterator<Department> collegeIterator = college.createIterator();
printCollege(collegeIterator);
}
}
private void printCollege(Iterator<Department> collegeIterator) {
while(collegeIterator.hasNext()) {
Department department = collegeIterator.next();
System.out.println(department.name);
}
}
}
}
17.4 迭代器模式在 JDK-ArrayList 集合应用的源码分析
- 系统提供 Iterator 接口
public interface Iterator<E> {}
- 内部类 Itr 充当具体实现迭代器 Iterator 的类, 作为 ArrayList 内部类
private class Itr implements Iterator<E> {}
- List 就是充当了聚合接口,含有一个 iterator() 方法,返回一个迭代器对象
public interface List<E> extends Collection<E> {
// ...
Iterator<E> iterator();
// ...
}
- ArrayList 是实现聚合接口 List 的子类,实现了 iterator()
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// ...
public Iterator<E> iterator() {
return new Itr();
}
// ...
}
- 迭代器模式解决了 不同集合(ArrayList ,LinkedList) 统一遍历问题
17.5 迭代器模式的注意事项和细节
优点
1) 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
2) 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
3) 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把 迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。 而如果遍历方式改变的话,只影响到了迭代器。
4) 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
缺点
1) 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类