这次笔者写写设计模式中较为简单的一个设计模式–Iterator(迭代器),为什么说它简单呢?主要的原因是它仅仅是用在了对于容器的遍历上。一般来说,一个及格的Programmer(程序员,注:Programmer != Coder,Coder是码农,其较之Programmer的层次低很多而且是一种蔑称,适用于自嘲但不适用于被人贴标签,此仅代表笔者的个人观点),都应该知道不同的数据结构之间(或者是不同容器之间)所使用到对内部存储数据访问的方式是不同的,即少有类似的遍历方式存在。譬如:数组的遍历是通过下标访问的方式、链表通过指针(或引用)访问的方式遍历、栈通过弹栈的方式遍历(虽然栈在正常情况下是不会进行遍历的)等等。
回到我们之前问题的回答上,由于它仅仅是用在了对于容器的遍历上我们说它简单,这里所说的简单并不是说它的实现简单(当然如果仅仅是list的话还是不会很难的,但是你可以试试对map的实现,呵呵!)而是说它在这么多的设计模式中没有那么复杂应用,比如笔者上一篇所写的Dynamic Proxy,它的应用面就太广了,就Spring来说对它的应用就足够初学的研究一段时间了。
现在我们来想想,我们为什么要用这种设计模式?它能带来什么样的便利?
笔者是这么想的:因为容器的遍历方式是各种各样的,为了减轻使用者的负担,我们应该想办法让它们统一起来,这样的统一应该是调用时的统一而不是实现方式的统一,实现过程对于使用来说应该是屏蔽的(黑盒),这样使用者就不用去纠结实现细节了。
在实现设计模式时需要经过几个版本的迭代,代码较多,主要是为了把思路体现出来。
1.Design a container of ArrayList & LinkedList(code version1.0)
现在笔者分别手写一个ArrayList和LinkedList的实现,ArrayList是基于数组实现的,LinkedList是基于链表实现的。
ArrayList.java
public class ArrayList{
private Object[] obj = new Object[5];
private int index = 0;
public void add(Object o){
if(index >= obj.length){
Object[] tmp = new Object[index + 20];
System.arraycopy(obj, 0, tmp, 0, obj.length);
obj = tmp;
}
obj[index++] = o;
}
public int size(){
return index;
}
}
特别地说下,链表的实现需要先构建一个Node类。
Node.java
public class Node{
private Object data;
private Node next;
public Node(Object data, Node next){
this.data = data;
this.next = next;
}
public void setData(Object data){
this.data = data;
}
public Object getData(){
return data;
}
public void setNext(Node next){
this.next = next;
}
public Node getNode(){
return next;
}
}
LinkedList.java
public class LinkedList{
private Node head = null;
private Node tail = null;
private int index = 0;//冗余的数据来加强性能
public void add(Object o){
Node n = new Node(o, null);//创建新的节点
if(null == head){
head = n;
tail = n;
}
tail.setNext(n);
tail = n;
index++;
}
public int size(){
return index;
}
}
以上就是自己实现的可以动态存放对象的两类容器。
2.How to traverse the elements in container(code version1.1)
可以看到笔者对于上面的实现是只对元素的添加做了方法处理,并不能做获取元素的方法处理。那我们开始想想应该如何遍历获取这些元素。
通常我们对于数组的元素获取多是通过下标的方式,链表的则是通过获取链表的头部逐个逐个地访问获取。这种思路并没有太多的创新的地方,因为一旦我们需要考虑起方法的复用时我们该怎么办?各种各样的实现我们明显是做不到对方法中的实现进行复用的,那我们换个思路,我们改成对方法这个入口的复用不就好了么!我们只要设计成一个接口让具体的类进行实现,这样就能做到黑盒了!
Iterator.java
public interface Iterator{
boolean hasNext();
Object next();
}
ArrayList.java
public class ArrayList{
private Object[] obj = new Object[5];
private int index = 0;
private Iterator l = new ArrayListIterator();//这样有singleton(单例设计)的味道
public void add(Object o){
if(index == obj.length){
Object[] tmp = new Object[index + 20];
System.arraycopy(obj, 0, tmp, 0, obj.length);
obj = tmp;
}
obj[index++] = o;
}
public int size(){
return index;
}
public Iterator iterator() {
return l;
}
//使用内部类可以访问外部类的数据成员
private class ArrayListIterator implements Iterator{
private int currentIndex = 0;
@Override
public boolean hasNext() {
if(this.currentIndex >= index) return false;
return true;
}
@Override
public Object next() {
return obj[currentIndex++];
}
}
}
LinkedList.java
public class LinkedList{
private Node head = null;
private Node tail = null;
private int index = 0;//冗余的数据来加强性能
private LinkedListIterator l = new LinkedListIterator();
public void add(Object o){
Node n = new Node(o, null);//创建新的节点
if(null == head){
head = n;
tail = n;
}
tail.setNext(n);
tail = n;
index++;
}
public int size(){
return index;
}
public Iterator iterator() {
return l;
}
private class LinkedListIterator implements Iterator{
private int currentIndex = 0;
private Node currentTarObj = null;
@Override
public boolean hasNext() {
if(currentIndex >= index) return false;
return true;
}
@Override
public Object next() {
if(null == head) return null;
if(0 == currentIndex){
currentTarObj = head;
} else {
currentTarObj = currentTarObj.getNext();
}
currentIndex++;
return currentTarObj.getData();
}
}
}
Test.java
public class Test{
public static void main(String[] args){
ArrayList arr = new ArrayList();
for (int i = 0; i < 26; i++) {
arr.add(i);
}
System.out.println("arrayList size: " + arr.size());
Iterator itr1 = arr.iterator();
while (itr1.hasNext()){
System.out.print(itr1.next() + " ");
}
System.out.println();
LinkedList ll = new LinkedList();
for (int i = 0; i < 10; i++) {
ll.add(i);
}
System.out.println("linkedlist size: " + ll.size());
Iterator itr2 = ll.iterator();
while (itr2.hasNext()){
System.out.print(itr2.next() + " ");
}
}
}
3.Continue to optimize(code version1.2)
仔细看上面的代码,我们很容易发现,ArrayList和LinkedList其实方法基本是一样的,一样的东西我们就可以考虑说能否把它们抽象出来成为接口。
当然你可能说这是笔者有意设置,但是你想想如果它们要实现的动作是差不多的,为什么我们不能考虑统一的命名规范呢?杂乱很可能在未来带来你我无法想象维护困难,难道不是吗?(再说了,如果未来如果是被你所知那还能称为未来吗?我们的想法最多算是猜测,再多的规划也不是一定会实现的,若说大势所趋就是未来,那么随着大势而去也难保总有人会葬送在这潮流中,这葬送的人中有你我吗?)
现在笔者来做一个稍高一点层次的抽象,让ArrayList和LinkedList成为这个抽象的一种具体实现而已,这也说明一个颠覆性的观点:从特殊推导出一般。
Collection.java
public interface Collection{
void add(Object o);
int size();
Iterator iterator();
}
ArrayList.java
public class ArrayList implements Collection{
private Object[] obj = new Object[5];
private int index = 0;
private Iterator l = new ArrayListIterator();//这样有singleton(单例设计)的味道
@Override
public void add(Object o){
if(index == obj.length){
Object[] tmp = new Object[index + 20];
System.arraycopy(obj, 0, tmp, 0, obj.length);
obj = tmp;
}
obj[index++] = o;
}
@Override
public int size(){
return index;
}
@Override
public Iterator iterator() {
return l;
}
//使用内部类可以访问外部类的数据成员
private class ArrayListIterator implements Iterator{
private int currentIndex = 0;
@Override
public boolean hasNext() {
if(this.currentIndex >= index) return false;
return true;
}
@Override
public Object next() {
return obj[currentIndex++];
}
}
}
LinkedList.java
public class LinkedList implements Collection{
private Node head = null;
private Node tail = null;
private int index = 0;//冗余的数据来加强性能
private LinkedListIterator l = new LinkedListIterator();
@Override
public void add(Object o){
Node n = new Node(o, null);//创建新的节点
if(null == head){
head = n;
tail = n;
}
tail.setNext(n);
tail = n;
index++;
}
@Override
public int size(){
return index;
}
@Override
public Iterator iterator() {
return l;
}
private class LinkedListIterator implements Iterator{
private int currentIndex = 0;
private Node currentTarObj = null;
@Override
public boolean hasNext() {
if(currentIndex >= index) return false;
return true;
}
@Override
public Object next() {
if(null == head) return null;
if(0 == currentIndex){
currentTarObj = head;
} else {
currentTarObj = currentTarObj.getNext();
}
currentIndex++;
return currentTarObj.getData();
}
}
}
Test.java
public class Test{
public static void main(String[] args){
Collection arr = new ArrayList();
for (int i = 0; i < 26; i++) {
arr.add(i);
}
System.out.println("arrayList size: " + arr.size());
Iterator itr1 = arr.iterator();
while (itr1.hasNext()){
System.out.print(itr1.next() + " ");
}
System.out.println();
Collection ll = new LinkedList();
for (int i = 0; i < 10; i++) {
ll.add(i);
}
System.out.println("linkedlist size: " + ll.size());
Iterator itr2 = ll.iterator();
while (itr2.hasNext()){
System.out.print(itr2.next() + " ");
}
}
}
这样我们不仅实现了方法复用,还实现了容器的可替换。
笔者这次就i到此为止了!
感谢各位的支持!
如果note有错漏,恳请各位留下评论指出,笔者会及时改正!
谢谢!