Java23种设计模式
设计模式并非类库,但类库中使用了设计模式
● java.util.Iterator是用于遍历元素集合的接口,这里使用了Iterator模式
● java.util.Observer是用于观察对象状态变化的接口,这里使用了Observer模式
● 以下的方法中使用了Factory Method模式
java.util.Calender类的getInstance方法
java.secure.SecureRandom类的getInstance方法
java.text.NumberFormat类的getInstance方法
● java.awt.Component和java.awt.Container这两个类中使用了Composite模式
第一部分 适应设计模式
第一章:Iterator模式---一个一个遍历:用于在数据集合中按照顺序遍历集合,迭代器
Iterator模式中的登场角色
◆Iterator(迭代器)
◆ConcreteIterator(具体的迭代器)
◆Aggregate(集合)
◆ConcreteAggregate(具体的集合)
不管实现如何变化,都可以使用Iterator:设计模式的作用就是帮助我们编写可复用性的类。
所谓的“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不需要对其他的组件进行修改或是只需很小的修改即可应对。
难以理解抽象类和接口:不要只使用具体类来编程,要优先使用抽象类和接口来编程
Aggregate和Iterator的对应
容易弄错“下一个”
next方法是“返回当前的元素,并指向下一个元素”
还容易弄错“最后一个”
hasNext方法在返回最后一个元素前会返回true,当返回了最后一个元素后则返回false,作用是“确认接下来是否可以调用next方法”
多个Iterator
“将遍历功能置于Aggregate角色之外”是Iterator模式的一个特征。
根据这个特征,可以针对一个ConcreteAggregate角色编写多个ConcreteIterator角色。
迭代器的种类多种多样
遍历的方法 •从最后开始向前遍历 •既可以从前向后遍历,也可以从后向前遍历(既有next方法也有previous方法)
•指定下标进行“跳跃式”遍历
不需要deleteIterator
没有被使用的对象实例将会自动被删除(垃圾回收,GC)。
相关的设计模式
◆Visitor模式
Iterator模式是从集合中一个一个取出元素进行遍历,但是并没有在Iterator接口中声明对取出的元素进行何种处理。
Visitor模式则是在遍历元素集合的过程中,对元素进行相同的处理。
◆Composite模式
Composite模式是具有递归结构的模式,在其中使用Iterator模式比较困难。
◆Factory Method模式
在Iterator方法中生成Iterator的实例时可能会使用Factory Method模式。
1 /** 2 *定义Book类,定义构造方法并初始化 3 */ 4 public class Book 5 { 6 private String name; 7 public Book(String name) { 8 this.name = name; 9 } 10 public String getName() { 11 return name; 12 } 13 }
1 /** 2 *定义BookShelf类 3 */ 4 import java.util.ArrayList; 5 6 /*public class BookShelf implements Aggregate 7 { 8 private Book[] books; 9 private int last = 0; 10 public BookShelf(int maxsize) { 11 this.books = new Book[maxsize]; 12 } 13 public Book getBookAt(int index) { 14 return books[index]; 15 } 16 public void appendBook(Book book) { 17 this.books[last] = book; 18 last++; 19 } 20 public int getLength() { 21 return last; 22 } 23 public Iterator iterator() { 24 return new BookShelfIterator(this); 25 } 26 }*/ 27 /** 28 *当书的数量超过最初指定的书架容量时,就无法继续向书架中添加书本了。 29 *编写用java.util.ArrayList修改程序 30 */ 31 public class BookShelf implements Aggregate 32 { 33 private ArrayList books; 34 public BookShelf(int initialsize) { 35 this.books = new ArrayList(initialsize); 36 } 37 public Book getBookAt(int index) { 38 return (Book)books.get(index); 39 } 40 public void appendBook(Book book) { 41 books.add(book); 42 } 43 public int getLength() { 44 return books.size(); 45 } 46 public Iterator iterator() { 47 return new BookShelfIterator(this); 48 } 49 }
1 /** 2 *表示集合的接口,该方法会生成一个用于遍历集合的迭代器 3 */ 4 public interface Aggregate 5 { 6 public abstract Iterator iterator(); 7 }
1 /** 2 *定义Iterator接口用于遍历集合中的元素 3 *其作用相当于循环语句中的循环变量 4 */ 5 public interface Iterator 6 { 7 /*当集合中存在下一个元素时,该方法返回true; 8 当集合中不存在下一个元素,即已经遍历至集合末尾时,该方法返回false 9 hasNext方法主要用于循环终止条件*/ 10 public abstract boolean hasNext(); 11 /*该方法返回的是集合中的一个元素,也可以返回下一个元素*/ 12 public abstract Object next(); 13 }
1 /** 2 *定义BookShelfIterator类 3 */ 4 public class BookShelfIterator implements Iterator 5 { 6 private BookShelf bookShelf; 7 private int index; 8 public BookShelfIterator(BookShelf bookShelf) { 9 this.bookShelf = bookShelf; 10 this.index = 0; 11 } 12 public boolean hasNext() { 13 if(index < bookShelf.getLength()) { 14 return true; 15 } else { 16 return false; 17 } 18 } 19 public Object next() { 20 Book book = bookShelf.getBookAt(index); 21 index++; 22 return book; 23 } 24 }
1 /** 2 *测试类Main 3 */ 4 public class Main 5 { 6 public static void main(String[] args) { 7 BookShelf bookShelf = new BookShelf(4); 8 bookShelf.appendBook(new Book("Around the World in 80 Days")); 9 bookShelf.appendBook(new Book("Bible")); 10 bookShelf.appendBook(new Book("Cinderella")); 11 bookShelf.appendBook(new Book("Daddy-Long-Legs")); 12 bookShelf.appendBook(new Book("East of Eden")); 13 bookShelf.appendBook(new Book("Frankenstein")); 14 bookShelf.appendBook(new Book("Gulliver's Travels")); 15 bookShelf.appendBook(new Book("Hamlet")); 16 Iterator it = bookShelf.iterator(); 17 while(it.hasNext()) { 18 Book book = (Book)it.next(); 19 System.out.println(book.getName()); 20 } 21 } 22 }
第二章:Adapter模式---加个“适配器”以便于复用
●类适配器模式(使用继承的适配器)
●对象适配器模式(使用委托的适配器)
Adapter模式中的登场角色
◆Target(对象)-->Print类
◆Client(请求者)-->Main类
◆Adaptee(被适配)-->Banner类
◆Adapter(适配)-->PrintBanner类
在类适配器模式中,Adapter角色通过继承来使用Adaptee角色,而在对象适配器模式中,Adapter角色通过委托来使用Adaptee角色。委托指将某个方法中的实例处理交给其他实例的方法。
什么时候使用Adapter模式
Adapter模式会对现有的类进行适配,生成新的类。通过该模式可以很方便地创建我们需要的方法群。当出现Bug时,由于我们很明确地知道Bug不在现有的类(Adaptee角色)中,所以只需调查扮演Adapter角色的类即可。这样一来,代码问题的排查就会变得非常简单。
如果没有现成的代码
使用Adapter模式可以在完全不改变现有代码的前提下使现有代码适配于新的接口(API)。此外,在Adapter模式中,并非一定需要现成的代码。只要知道现有类的功能,就可以编写出新的类。
版本升级与兼容性
功能完全不同的类
Adaptee角色和Target角色的功能完全不同时,Adapter模式无法使用。
相关的设计模式
◆Bridge模式
Adapter模式用于连接接口(API)不同的类,而Bridge模式则用于连接类的功能层次结构与实现层次结构。
◆Decorator模式
Adapter模式用于填补不同接口(API)之间的缝隙,而Decorator模式则是在不改变接口(API)的前提下增加功能。
1 /** 2 *Banner类 3 */ 4 public class Banner 5 { 6 private String string; 7 public Banner(String string) { 8 this.string = string; 9 } 10 public void showWithParen() { 11 System.out.println("(" + string + ")"); 12 } 13 public void showWithAster() { 14 System.out.println("*" + string + "*"); 15 } 16 }
1 2 /** 3 *定义FileIO接口(Target角色)中声明了将属性集合保存至文件的方法, 4 *并假设FileProperties类会实现这个FileIO接口 5 */ 6 import java.io.*; 7 public interface FileIO 8 { 9 public void readFormFile(String filename) throws IOException; 10 public void writeToFile(String filename) throws IOException; 11 public void setValue(String key, String value); 12 public String getValue(String key); 13 }
1 /** 2 *Print接口是"需求"的接口 3 *使用继承的适配器 4 */ 5 /*public interface Print 6 { 7 public abstract void printWeak(); 8 public abstract void printStrong(); 9 }*/ 10 /** 11 *使用委托的适配器 12 */ 13 public abstract class Print 14 { 15 public abstract void printWeak(); 16 public abstract void printStrong(); 17 }
1 /** 2 *PrintBanner类扮演适配器的角色 3 *使用继承的适配器 4 */ 5 /*public class PrintBanner extends Banner implements Print 6 { 7 public PrintBanner(String string) { 8 super(string); 9 } 10 public void printWeak() { 11 showWithParen(); 12 } 13 public void printStrong() { 14 showWithAster(); 15 } 16 }*/ 17 /** 18 *使用委托的适配器 19 */ 20 public class PrintBanner extends Print 21 { 22 private Banner banner; 23 public PrintBanner(String string) { 24 this.banner = new Banner(string); 25 } 26 public void printWeak() { 27 banner.showWithParen(); 28 } 29 public void printStrong() { 30 banner.showWithAster(); 31 } 32 }
1 /** 2 *用Adapter模式将一个属性集合保存至文件中的FileProperties类 3 */ 4 import java.io.*; 5 import java.util.*; 6 public class FileProperties extends Properties implements FileIO 7 { 8 public void readFormFile(String filename) throws IOException { 9 load(new FileInputStream(filename)); 10 } 11 public void writeToFile(String filename) throws IOException { 12 store(new FileOutputStream(filename),"written by FileProperties"); 13 } 14 public void setValue(String key,String value) { 15 setProperty(key,value); 16 } 17 public String getValue(String key) { 18 return getProperty(key,""); 19 } 20 }
1 /** 2 *Main类是通过扮演适配器角色的PrintBanner类来弱化(带括号) 3 *或是强化Hello(带*号)字符串的显示 4 */ 5 /*public class Main 6 { 7 public static void main(String[] args) { 8 Print p = new PrintBanner("Hello"); 9 p.printWeak(); 10 p.printStrong(); 11 } 12 }*/ 13 import java.io.*; 14 public class Main 15 { 16 public static void main(String[] args) { 17 FileIO f = new FileProperties(); 18 try { 19 f.readFormFile("file.txt"); 20 f.setValue("year","2017"); 21 f.setValue("month","6"); 22 f.setValue("day","9"); 23 f.writeToFile("newfile.txt"); 24 } catch(IOException e) { 25 e.printStackTrace(); 26 } 27 } 28 }
file.txt
year=1995
生成的newfile.txt
#written by FileProperties
#Fri Jun 09 20:04:48 CST 2017
day=9
year=2017
month=6
第三章:Template Method模式---将具体处理交给子类
在父类中定义处理流程的框架,在子类中实现具体处理的模式
◆AbstractClass(抽象类)
AbstractClass角色不仅负责实现模板方法,还负责声明在模板方法中所用的抽象方法。这些抽象方法由子类负责ConcreteClass角色负责实现。
◆ConcreteClass(具体类)
该角色负责具体实现AbstractClass角色中定义的抽象方法。这里实现的方法将会在AbstractClass角色的模板方法中被调用。
可以使逻辑处理通用化
父类与子类之间的协作
父类与子类的一致性
使用父类类型的变量保存子类实例的优点是,即使没有用instanceof等指定子类的种类,程序也能正常工作。
无论在父类类型的变量中保存哪个子类的实例,程序都可以正常工作,这种原则称为里氏替换原则(LSP)。它是通用的继承原则。
相关的设计模式
◆Factory Method模式
Factory Method模式是将Template Method模式用于生成实例的一个典型例子。
◆Strategy模式
在Template Method模式中,可以使用继承改变程序的行为。这是因为Template Method模式在父类中定义程序行为的框架,在子类中决定具体的处理。
与此相对的是Strategy模式,它可以使用委托改变程序的行为。与Template Method模式中改变部分程序行为不同的是,Strategy模式用于替换整个算法。
父类对子类的要求
◆在子类中可以使用父类中定义的方法
◆可以通过在子类中增加方法以实现新的功能
◆在子类中重写父类的方法可以改变程序的行为
声明抽象方法的目的:
◆期待子类去实现抽象方法
◆要求子类去实现抽象方法
抽象类的意义
父类与子类的协作
1 /** 2 *只实现了display方法的抽象类 3 */ 4 public abstract class AbstractDisplay 5 { 6 public abstract void open();//交给子类去实现的抽象方法 7 public abstract void print();//交给子类去实现的抽象方法 8 public abstract void close();//交给子类去实现的抽象方法 9 public final void display() {//本抽象类中实现的display方法 10 open();//首先打开 11 for(int i = 0; i < 5; i++) { 12 print(); 13 } 14 close();//然后关闭 15 } 16 }
1 /** 2 *实现了open、print、close方法的类 3 */ 4 public class CharDisplay extends AbstractDisplay 5 { 6 private char ch;//需要显示的字符 7 public CharDisplay(char ch) { 8 this.ch = ch; 9 } 10 public void open() { 11 System.out.print("<<"); 12 } 13 public void print() { 14 System.out.print(ch); 15 } 16 public void close() { 17 System.out.print(">>"); 18 } 19 }
1 /** 2 *实现了open、print、close方法的类 3 */ 4 public class StringDisplay extends AbstractDisplay 5 { 6 private String string; //需要显示的字符串 7 private int width;//以字节为单位计算出的字符串长度 8 public StringDisplay(String string) { 9 this.string = string; 10 this.width = string.getBytes().length; 11 } 12 public void open() { 13 printLine(); 14 } 15 public void print() { 16 System.out.println("|" + string + "|"); 17 } 18 public void close() { 19 printLine(); 20 } 21 private void printLine() { 22 System.out.print("+");//显示表示方框的角的"+" 23 for(int i = 0; i < width; i++) { 24 System.out.print("-"); 25 } 26 System.out.println("+"); 27 } 28 }
1 /** 2 *测试程序行为的类 3 */ 4 public class Main 5 { 6 public static void main(String[] args) { 7 //生成一个持有'H'的CharDisplay类的实例 8 AbstractDisplay d1 = new CharDisplay('H'); 9 //生成一个持有"Hello World!"的StringDisplay类的实例 10 AbstractDisplay d2 = new StringDisplay("Hello World!"); 11 AbstractDisplay d3 = new StringDisplay("你好 世界!"); 12 d1.display(); 13 d2.display(); 14 d3.display(); 15 } 16 }
第四章:Factory Method模式---将实例的生成交给子类
Factory Method模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责。
这样就可以将生成实例的框架和实际负责生成实例的类解耦。
Factory Method模式中的登场角色
◆Product(产品)
◆Creator(创建者)
不用new关键字来生成实例,而是调用生成实例的专用方法来生成实例,这样就可以防止父类与其他具体类耦合。
◆ConcreteProduct(具体的产品)
◆ConcreteCreator(具体的创建者)
生成实例-----方法的三种实现方式
◆指定其为抽象方法
abstract class Factory {
public abstract Product createProduct(String name);
…
}
◆为其实现默认处理
class Factory {
public Product createProduct(String name) {
return new Product(name);
}
…
}
不过,这时是使用new关键字创建出实例的,因此不能将Product类定义为抽象类。
◆在其中抛出异常
class Factory {
public Product createProduct(String name) {
throw new FactoryMethodRuntimeException();
}
…}
不过,需要另外编写FactoryMethodRuntimeException异常类
相关的设计模式
◆Template Method模式
Factory Method模式是Template Method 的典型应用。在示例程序中,create方法就是模板方法。
◆Singleton模式
在多数情况下我们可以将Singleton模式用来扮演Creator角色(或是ConcerteCreator角色)的类。
◆Composite模式
有时可以将Composite模式用于Product角色(或是ConcerteProduct角色)
◆Iterator模式
有时,在Iterator模式中使用iterator方法生成Iterator的实例时会用Factory Method模式。
1 /** 2 *只定义抽象方法use的抽象类 3 */ 4 package framework;//生成实例的框架(framework包) 5 6 public abstract class Product 7 { 8 public abstract void use(); 9 }
1 /** 2 *实现了create方法的抽象类 3 */ 4 package framework; 5 6 public abstract class Factory 7 { 8 public final Product create(String owner) { 9 Product p = createProduct(owner); 10 registerProduct(p); 11 return p; 12 } 13 protected abstract Product createProduct(String owner); 14 protected abstract void registerProduct(Product product); 15 }
1 /** 2 *实现了use方法的类 3 */ 4 package idcard; 5 import framework.*; 6 7 public class IDCard extends Product 8 { 9 private String owner; 10 private int serial; 11 IDCard(String owner, int serial) { 12 System.out.println("制作" + owner + "(" + serial + ")" + "的ID卡。"); 13 this.owner = owner; 14 } 15 public void use() { 16 System.out.println("使用" + owner + "(" + serial + ")" + "的ID卡。"); 17 } 18 public String getOwner() { 19 return owner; 20 } 21 public int getSerial() { 22 return serial; 23 } 24 }
1 /** 2 *实现了createProduct、registerProduct方法的类 3 */ 4 package idcard; 5 import framework.*; 6 import java.util.*; 7 8 public class IDCardFactory extends Factory 9 { 10 /*private List owners = new ArrayList(); 11 protected Product createProduct(String owner) { 12 return new IDCard(owner); 13 } 14 protected void registerProduct(Product product) { 15 owners.add(((IDCard)product).getOwner()); 16 } 17 public List getOwners() { 18 return owners; 19 }*/ 20 private HashMap database = new HashMap(); 21 private int serial = 100; 22 protected synchronized Product createProduct(String owner) { 23 return new IDCard(owner,serial++); 24 } 25 protected void registerProduct(Product product) { 26 IDCard card = (IDCard)product; 27 database.put(new Integer(card.getSerial()),card.getOwner()); 28 } 29 public HashMap getDatabase() { 30 return database; 31 } 32 }
1 /** 2 *测试程序行为的类 3 */ 4 import framework.*; 5 import idcard.*; 6 7 public class Main 8 { 9 public static void main(String[] args) { 10 Factory factory = new IDCardFactory(); 11 Product card1 = factory.create("小明"); 12 Product card2 = factory.create("小红"); 13 Product card3 = factory.create("小刚"); 14 card1.use(); 15 card2.use(); 16 card3.use(); 17 } 18 }