系统融合(二) -- 迭代器模式
聚合类型兼容性问题
接上一次分享《系统融合 -- 适配器模式》,在对A、B两个系统进行融合的过程中,可以使用“适配器模式”把两类业务类似但接口定义不同的接口适配为同一个系统。在两个系统融合过程中,还经常遇到另一种情况:A系统返回的商品列表是ArrayList类型,B系统返回的商品列表是数组类型。
//A系统获取商品列表接口 public List<Product> getProdoucts(){ //省略业务代码 } //B系统获取商品列表接口 public Product [] getProdoucts(){ //省略业务代码 }
这就是所谓的“聚合类型兼容性问题”。这时为了统一接口类型,可以在“适配器系统”把ArrayList转换成数组,或者把数组转换成ArrayList。但这不是最优雅的方式,我们还可以使用“迭代器模式”对两个接口进行兼容。Java中得聚合类型:数组、List、Set、Map等。
在使用“迭代器模式”解决这个兼容性问题之前,首先来看看什么是“迭代器模式”。
迭代器模式
迭代器模式提供一种顺序访问一个聚合对象中的各个元素的方法,而又不暴露其内部的表象。把遍历聚合中各个元素的任务移交到“迭代器”上,满足OO设计原则中的“单一责任原则”。另外具体的“迭代器”都实现自一个统一的接口(Iterator),可以兼容不同的聚合类型遍历(这就是解决本文开头“兼容性”问题的关键)。
简单的理解,就是把聚合类型中遍历每个成员的任务剥离出来,生成“迭代器”,这些迭代器都实现自同一个接口。类图关系:
从类图上看,该模式主要有4类角色:
抽象的聚合:AbsAggregate,可以是抽象类 也可以是接口。一般都会定义一个抽象方法,获取迭代器。
具体的聚合:ConcreteAggregate,实现或继承自AbsAggregate。一般都会实现AbsAggregate中的抽象方法,获取具体的迭代器。
抽象的迭代器:Iterator可以是抽象类 也可以是接口。一般最少有两个抽象方法,hasNext()和next()方法,用于遍历聚合中的元素。
具体的迭代器:ConcreteIterator,实现或继承自Iterator。对hasNext()和next()方法进行具体的实现。其构造过程依赖“具体的聚合”,也就是说每个“具体的聚合”,一般都会对应一个自己 “具体的迭代器”。
示例展示
回到文章开头,开始使用“迭代器模式”对A、B两个系统融合过程中,对两个不同的获取商品列表接口进行融合。为了方便理解,实现过程按照“迭代器模式”的4类角色 分类进行:
抽象的聚合:本示例中抽象的聚合是ProdcutAdapter接口,里面只定义了一个createIterator()的抽象方法:
public interface ProdcutAdapter { //批量获取商品接口 Iterator<Product> createIterator(); }
具体的聚合:本示例中有两个系统进行融合,对应有两个具体的聚合:ProdcutAdapterAImpl(对应List类型的商品列表接口)、ProdcutAdapterBImpl(对应数组类型的商品接口)。核心是对createIterator方法的实现:
public class ProdcutAdapterAImpl implements ProdcutAdapter { public Iterator<Product> createIterator() { List<Product> products =getProdoucts(); Iterator iterator = new ListIterator(products); return iterator; } //模拟从A系统获取List类型的商品列表 public List<Product> getProdoucts(){ //省略调用A系统方法,获取数组类型的商品列表 Product p1 = new Product(); p1.setId(1000); p1.setName("A系统1000商品"); Product p2 = new Product(); p2.setId(1001); p2.setName("A系统1001商品"); List<Product> products = new ArrayList(); products.add(p1); products.add(p2); return products; } } public class ProdcutAdapterBImpl implements ProdcutAdapter { public Iterator<Product> createIterator() { Product [] array = getProdoucts(); Iterator iterator = new ArrayIterator(array); return iterator; } //模拟从B系统获取数组类型的商品列表 public Product [] getProdoucts(){ //省略调用B系统方法,获取数组类型的商品列表 Product p3 = new Product(); p3.setId(1003); p3.setName("B系统1003商品"); Product p4 = new Product(); p4.setId(1004); p4.setName("B系统1004商品"); Product [] array = {p3,p4}; return array; } }
抽象的迭代器:Iterator,这里只定义迭代器的两个核心方法 hasNext和next:
public interface Iterator<E> { boolean hasNext(); Object next(); }
具体的迭代器:本示例中有两类聚合,List和数组。对应的会创建两个具体的迭代器:ListIterator、ArrayIterator
public class ListIterator<E> implements Iterator{ private List<E> products; int pos=0;//当前位置 //外部迭代器,构造函数需要传入一个待迭代的集合类型 public ListIterator(List<E> products) { this.products = products; } public boolean hasNext() { if (pos >= products.size()) { return false; } else { return true; } } public Object next() { E product = products.get(pos); pos = pos + 1; return product; } } public class ArrayIterator<E> implements Iterator{ private E [] products; int pos=0;//当前位置 //外部迭代器,构造函数需要传入一个待迭代的集合类型 public ArrayIterator(E[] products) { this.products = products; } public boolean hasNext() { if (pos >= products.length) { return false; } else { return true; } } public Object next() { E product = products[pos]; pos = pos + 1; return product; } }
到这里“迭代器模式”的4类角色的实现完成,下面开始测试:
public class Main { public static void main(String[] args) { ProdcutAdapter prodcutAdapterA = new ProdcutAdapterAImpl(); ProdcutAdapter prodcutAdapterB = new ProdcutAdapterBImpl(); //获取迭代器,两个老系统接口被统一为一个迭代器类型 Iterator iteratorA = prodcutAdapterA.createIterator(); Iterator iteratorB = prodcutAdapterB.createIterator(); //展示商品列表 showProducts(iteratorA); showProducts(iteratorB); } //模拟商品列表展示 public static void showProducts(Iterator iterator){ while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println(" "); } }
执行main方法,打印结果为:
Product{id=1000, name='A系统1000商品', category='null', venderId=null} Product{id=1001, name='A系统1001商品', category='null', venderId=null} Product{id=1003, name='B系统1003商品', category='null', venderId=null} Product{id=1004, name='B系统1004商品', category='null', venderId=null}
这里main方法是展示的如何让“客户端”无感知的调用A、B两个系统的获取商品列表方法,并采用统一的方法进行打印。换句话说“客户端”无需关心商品列表是List类型 还是 数组类型。
到这里,使用“迭代器模式”已经优雅的解决是两个系统“融合”过程中,聚合类型不兼容的问题。
Java中的迭代器
Java的API中对大部分的聚合类型都已经默认实现了自己的迭代器,统一实现自接口java.util.Iterator,相比本示例中定义的Iterator,java.util.Iterator多了一个remove方法:
public interface Iterator<E> { boolean hasNext(); E next(); default void remove() }
在上述示例中,我们自己实现了ArrayList类型的迭代器ArrayIterator,这里仅仅为了做示例演示使用。其实java API中ArrayList,已经有自己的迭代器实现,直接调用其iterator()方法即可。
如果本示例改用java.util.Iterator作为抽象的迭代器,ArrayIterator几乎不用改,去掉ArrayIterator实现,在ProdcutAdapterAImpl中把:
Iterator iterator = new ListIterator(products);
改为:
Iterator iterator = products.iterator();
Java api中几乎已为所有的聚合类型创建了自己的迭代器,并且都实现自java.util.Iterator接口。如果要扩展自定义聚合类型的迭代器,直接实现这个接口即可,这样做的好处是可以跟java api中的聚合类型的迭代器完全兼容。
内、外部迭代器
最后再提下内部迭代器和外部迭代器,本次示例中使用独立的类创建具体的迭代器,这属于外部迭代器。Java API中的实现都是内部迭代器:迭代器实现类做为聚合的一个内部类。这两种方式有各自的优缺点:
外部迭代器:优点 “单一责任原则”更彻底,具备更好的复用性。缺点 暴露聚合的内部成员。创建迭代器时,需要具体的聚合类型作为参数:
Iterator iterator = new ListIterator(products);
内部迭代器:优缺点刚好与外部迭代器相反。创建迭代器时,不需要参数:
Iterator iterator = products.iterator();
Java API为了防止暴露内部数据结构,一般都采用内部迭代器。
上一篇: Maven使用slf4j-log4j12包冲突的问题解决
下一篇: SimpleITK 图像配准