在学习Thinking in Java的书籍的容器类章节时,接触到迭代器模式和适配器模式的知识。
结合《大话设计模式》的书籍内容,在此写下相关知识的理解和总结。
迭代器模式的目的是创建一个轻量级对象(创建代价小的对象),能忽略序列的具体类型和底层结构地对序列进行遍历和选择元素。正如《大话设计模式》书中所述,提供了一种方法顺序访问(遍历)一个聚合对象(容器)的各个元素,而不暴露该容器的内部表示。
何时该考虑使用迭代器(Iterator)模式?
1)不论是什么容器,容器里面有什么类别的对象,都需要被遍历的时候,则最好创建一个迭代器的类型,统一遍历时所调用的接口;
2)需要对容器有多种方式的遍历时,可以考虑创建一个迭代器的接口,去通过改变不同的实现而不用修改调用迭代器的调用代码去实现不同的遍历方式;
标准的Iterator的接口:
Interface Iterator{
object Next();
bool HasNext();
void remove();
}
所以,如果在Java中想实现自定义容器类的遍历,有两个方法:
1)继承AbstractCollection类,并实现其中的Iterator()和size(),这样可以用foreach语法去遍历这个collection;
2)自己实现Iterator(),利用Iterator的hasNext(),Next()函数实现遍历;如果想用这个方法也能使用foreach语法,需要继承Iterable类实现。
注意,上述方法中iterator()方法返回Iterator的方式是通过返回匿名内部类Iterator的匿名实例。只要一个类实现了Iterable接口,就能对其使用foreach语法遍历。Java中,Collection类都是实现了Iterable接口的,但Map并没有实现。另外,数组也可以使用foreach语法,但是特别的是,数组不是Iterable的实现。
在Java当中,Collection只是一个接口类,并没有提供任何的实现细节。实现Collection必须提供Iterator方法的实现。而java.util.AbstractCollection类提供了Collection的默认实现,所以可以直接继承该类,以避免代码重复,但是AbstractCollection需要继承者实现Iterator()和size(),因为其并没有实现。
另外,如果一个类已经继承了其它父类,就不能再继承AbstractCollection类了,这种情况下,如果想实现Collection,就必须实现该接口的所有方法,变得十分麻烦。所以,在上述情况下,实现Iterator是最明智的方法。
方法1的代码:
import java.util.*;
public class SelfDeineCollection extends AbstractCollection<Integer> {
private int[] integers_ = new int[]{1,2,3,4,5};
public int size(){
return integers_.length;
}
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int index_ = 0;
public boolean hasNext() {
return index < integers_.length;
}
public int next(){
return integers_[index_++];
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public static main(String[] args) {
SelfDefineCollection temp_ins = new SelfDefineCollection();
for (int temp : temp_ins){
System.out.println(temp);
}
}
}
方法二(不继承Iterable类):
import java.util.*;
class SelfDefineSequence {
protected int[] member_seq_ = new int[]{1,2,3,4,5};
}
public class SelfDefineIterator extends SelfDefineSequence{
public Iterator<Integer> iterator() {
return new Iterator<Integer> () {
private int index_ = 0;
public boolean hasNext() {
return index_ < member_seq_.length;
}
public int next() {
return member_seq_[index_++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args){
SelfDefineIterator temp_ins = new SelfDeineIterator();
Iterator temp_iter = temp_ins.iterator();
while (temp_iter.hasNext()) {
int temp = temp_iter.next();
System.out.println(temp);
}
}
}
方法二(继承Iterable类):
其实Iterable类是一个接口,包含一个能产生Iterator的iterator()方法。而foreach语法是利用Iterable接口实现的。因此如果创建了任何实现Iterable的类,皆可以将它用于foreach语法中。
import java.util.*;
public class IterableClass implements Iterable<String> {
protected String[] words_ = ("And that is how we know the Earth to be banana-shaped.").split(" ");
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index_ = 0;
public boolean hasNext() {
return index_ < words_.length;
}
public String next() {
return words_[index_++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args){
IterableClass temp_ins = new IterableClass();
for (String temp : temp_ins){
System.out.println(temp);
}
}
}
根据前述定义,迭代器通过修改对Iterator接口的修改来实现多种不同的遍历方法(在Java中的体验是对匿名内部类的匿名实例的实现的不同)。但如果想提供不同的迭代方式供程序选择,则可以使用适配器(Adaptor)模式。适配器模式主要适用于:当目前你已经拥有一个接口,且这个接口不能被覆盖或者修改,需要使用与此接口有相同目的但处理方式的接口(像《大话设计模式》的例子,相同的目的是:明白教练的策略,但不同的方式是本地球员是直接理解,而外国球员需要添加一个适配器,充当翻译的作用,再去理解,详见图1),这时候可以通过适配器模式像添加补丁一样新增一个新的接口而非覆盖原接口。
图1
像刚刚说到的例子,相同的目的是:实现对数组的遍历。不同的方式是:原来的顺序遍历,现在想要添加逆序遍历。
实现代码如下:
import java.util.*;
class RevertableArrayList<T> extends ArrayList<T> {
public RevertableArrayList(Collection<T> c) {
super (c);
}
private new_iterable_ = new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = this.size() -1;
public boolean hasNext() { return current > -1}
public T next() {return this.get(current--)};
public void remove {System.out.println("Not support");}
};
}
}
public Iterable<T> reversed() { // this is a translator/ adapter. In this situation, it needs to return a iterable that can be used in foreach sentence.
return new_iterable_;
}
}
public class AdaptorMethod{
public static main(String[] args){
RevertableArrayList<String> ral = new RevertableArrayList<String>(Arrays.asList("To be or not to be").split(" ")));
for (String temp : ral.reversed())
System.our.println(temp);
}
}