1. “Iterator模式——一个一个遍历”:从含有多个集合中将各个元素逐一取出
在java中显示arr数组的元素时,可用for循环遍历数据
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
这里的i++作用让i的值在每次循环后自增1,也就从头至尾逐一遍历数据元素
将这里的循环变量i的作用抽象化、通用化后形成的模式,在设计模式中成为Iterator模式
案例:将书放在书架中,并将书的名字按顺序显示出来
名字 | 说明 |
---|---|
Aggregate | 表示集合的接口 |
Iterator | 遍历集合的接口 |
Book | 表示书的类 |
BookShelf | 表示书架的类 |
BookShelfIterator | 遍历书架的类 |
Main | 测试程序行为的类 |
Aggregate接口
public interface Aggregate{
public abstract Iterator iterator();//生成一个用于遍历集合的迭代器
}
Iterator接口
public interface Iterator{
public abstract boolean hasNext();//当集合中存在下一个元素时,返回true,否则返回false;主要用于循环终止条件
public abstract Objct next();//返回集合中的一个元素
}
Book类:
public class Book{
private String name;
public Book(String name){
this.name = name;
}
public String getName(){
return name;
}
}
BookShelf类
public class BookShelf implements Aggregate{
private Book[] books;
private int last = 0;
public BookShelf(int maxsize){
this.books = new Book[maxsize];
}
public Book getBookAt(int index){
return books[index];
}
public void appendBook(Book book){
this.books[last] = book;
last++;
}
public int getLength(){
return last;
}
public Iterator iterator(){
return new BookShelfIterator(this);
}
}
BookShelfIterator类
public class BookShelfIterator implements Iterator{
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf){
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext(){
if(index<bookShelf.getLength()){
return true;
}else{
return false;
}
}
public Object next(){
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
Main类
public class Main{
public static void main(String[] args){
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the world in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
Iterator it = bookShelf.iterator();
while(it.hasNext()){
Book book = (Book)it.next();
System.out.println(book.getName())
}
}
}
Iterator模式中的角色
- Iterator(迭代器)
负责定义按顺序逐个遍历元素的接口(API)。在案例中有Iterator接口扮演这个角色,它定义了hasNext和next两个方法。hasNext判断是否存在下一个元素,next用于获取该元素。 - ConcreteIterator(具体的迭代器)
负责实现Iterator角色定义的接口(API)。在案例中由BookShelfIterator类扮演。该角色中包含了遍历集合所需要的信息。在案例中BookShelf类的实例保存在bookShelf字段中,被指向的书的下标保存在index字段中。 - Aggregate(集合)
负责定义创建Iterator角色的接口(API)。这个接口是一个方法,会创建出”按顺序访问保存在我内部的人“。在案例中那个Aggregate接口扮演这个角色,它里面定义了iterator方法。 - ConcreteAggregate(具体的集合)
该角色负责实现Aggregate角色所定义的接口。它会创建出具体的Iterator角色,在案例中BookShelf类扮演,实现了iterator方法。
引用Iterator这种模式的优点:可以将遍历和实现分离开来
while(it.hasNext()){
Book book = (Book)it.next();
System.out.println(book.getName())
}
这里只使用到Iterator的hasNext和next方法。这里while不依赖BookShelf的实现,如果后期放弃用数组来管理书本,而采用Vector,对while循环没有影响。
重点思想:不要只是用具体类来编程,要优先使用抽象类和接口来编程
2. “Adapter模式——加个适配器以便复用”:可用来连接具有不同接口的类
用于填补“现有的程序”和“所需程序”之间的差异的设计模式就是Adapter模式(也叫wrapper模式,有包装器的意思)
Adapter模式分类:
- 类适配器模式(使用继承的适配器)
- 对象适配器模式(使用委托的适配器)
使用继承的适配器案例:
将输入的字符串显示为(Hello)或是Hello的程序
电源的比喻 | 示例程序 | |
---|---|---|
实际情况 | 交流100V | Banner类(showWithParen、showWithAster) |
变换装置 | 适配器 | PrintBanner类 |
需求 | 直流12V | print接口(printWeak、printStrong) |
print接口
public interface print{
public abstract void printWeak();
public abstract void printStrong();
}
printBanner类:继承了Banner类,又实现了print接口
public class PrintBanner extends Banner implements Print{
public PrintBanner(String string){
super(string);
}
public void printWeak(){
showWithParen();
}
public void printStrong(){
showWithAster();
}
}
Main类
public class Main{
public static void main(String[] args){
Print p = new PrintBanner("Hello");
p.printWeak();
p.printStrong();
}
}
在Main类中,使用Pint接口。对Main中的代码而言,Banner类、showWithParen方法和showWithAster方法被完全隐藏起来了。其并不用知道PrintBanner类如何实现的,这样就可以在不用对Main类进行修改的情况下改变PrintBanner类的具体实现。
使用委托的适配器案例:
委托的解释:通俗来说“交给其他人”,在java中,委托就是指将某个方法中的实际处理交给其他实例的方法。
这里假设Print不是接口而是类,即利用Banner类实现一个类,该类的方法和Print类的方法相同。由于java中无法同时继承两个类,因此无法将PrintBanner类分别定义为Print类和Banner类的子类。
PrintBanner类的banner字段保存了Banner类的实例。该实例实在PrintBanner类的构造函数中生成的。然后PrintWeak方法和printStrong方法会通过banner字段调用Banner类的showWithParen方法和showWithAster方法。与上面的调用从父类中继承的方法不同,这次通过字段来调用这两种方法。这样就形成了一种委托关系。
Print类:
public abstract class Print{
public abstract void printWeak();
public abstract void printStrong();
}
PrintBanner类:
public class PrintBanner extends Print{
private Banner banner;
public PrintBanner(String string){
this.banner = new Banner(string);
}
public void printWeak(){
banner.showWithParen();
}
public void printStrong(){
banner.showWithAster();
}
}
Adapter模式中的角色
- Target(对象)
负责定义所需要的方法,以电流为例,即为直流12V。在案例中由Print接口(使用继承方法)和Print类(使用委托时)扮演此角色。 - Client(请求者)
负责使用Target角色所定义的方法进行具体处理。在案例中由Main类扮演此角色。 - Adaptee(被适配)
Adaptee是一个持有既定方法的角色,在案例中,由Banner类扮演此角色。如果Adaptee角色中的方法与Target角色的方法相同(也就是说家庭使用的电压就是12V)就不需要Adapter角色了。 - Adapter(适配)
使用Adapter角色的方法来满足Target角色的需求,这就是Adapter模式的作用,就是将交流100V电转换为直流12V,在案例中由PrintBanner类扮演。
什么时候适合用Adapter模式?
当用到现有的类,特别是当现有的类已经被充分测试过了,bug很少,而且已经被用于其他软件之中时,更愿意将这些类作为组件重复利用。
Adapter模式会对现有的类进行适配,生成新的类。通过该模式可以很方便的创建我们需要的方法群。当出现Bug时,可以很明确的知道Bug不在现在的类中,所以只需要调查扮演Adapter角色的类即可。
Adapter模式可用于版本升级与兼容性