第8篇-JAVA面向對象-設計模式Ⅳ
每篇一句 : 想象是程序的創作之源
初學心得 : 平靜的海洋練不出熟練的水手
(筆者:JEEP/711)[JAVA筆記 | 時間:2017-04-11| JAVA面向對象 Ⅳ]
JAVA設計模式
什麼是JAVA設計模式以及作用?
設計模式是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結
使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性,本篇將介紹10種JAVA常用設計模式
1.JAVA 設計模式 - 單例設計模式
單例模式是一種創建模式,這種模式只涉及一個單獨的類,它負責創建自己的對象
該類確保只創建單個對象,這個類提供了一種訪問其唯一對象的方法
單例模式目的是爲了整個應用中有且只有一個實例,所有指向該類型實例的引用都指向這個實例
保證對象不能再類的外部被隨意實例化,解決方法:將構造器進行私有化處理
保證類創建的過程發生在類的內部,還有保證在類的外部能拿到在類的內部初始化的對象單例模式類型:餓漢式單例,懶漢式單例
Singleton(餓漢模式)
餓漢模式,特點是程序加載類的時候比較慢,但運行時獲得對象的速度比較快,它從加載到應用結束會一直佔用資源
餓漢模式代碼:1.class Singleton { 2. public class SingletonDemo { 3. public static void main(String[] args) { 4. Singleton1 s1 = Singleton1.getInstance(); 5. Singleton1 s2 = Singleton1.getInstance(); 6. System.out.println(s1==s2); //true 7. Singleton2 s3 = Singleton2.getInstance(); 8. Singleton2 s4 = Singleton2.getInstance(); 9. System.out.println(s3==s4); //true 10. } 11.} 12.class Singleton1{ 13. private static final Singleton1 instance = new Singleton1(); //在內部準備好一個對象 14. public static Singleton1 getInstance(){ 15. return instance; 16. } 17. private Singleton1(){} 18. public void show(){ 19. System.out.println("Singleton1"); 20. } 21.} 22.class Singleton2 { 23. private static Singleton2 instance; 24. //將instance傳遞到外部去 25. public static Singleton2 getInstance(){ 26. if(instance == null){ 27. instance = new Singleton2(); 28. } 29. return instance; 30. } 31. private Singleton2(){} 32.}Singleton(懶漢模式)
懶漢模式特點,程序是運行時獲得對象的速度比較慢,但加載類的時候比較快
它在整個應用的生命週期只有一部分時間在佔用資源
懶漢模式代碼:1.class Singleton{ 2. private static Singleton instance = null; 3. public static Singleton getInstance(){// 將instance傳遞到外部去 4. if(instance == null){ 5. instance = new Singleton(); 6. } 7. return instance; 8. } 9. private Singleton(){} 10.} 11.public static Singleton2 getInstance(){ 12. if(instance == null){ 13. synchronized(Singleton2.class){ 14. if(instance == null){ 15. instance = new Singleton2(); 16. } 17. } 18. } 19. return instance; 20. }Singleton(餓漢模式) & Singleton(懶漢模式) 區別
(1)這兩種模式對於初始化較快,佔用資源少的輕量級對象來說,沒有多大的性能差異,選擇懶漢式或餓漢式都沒有問題
但是對於初始化慢,佔用資源多的重 量級對象來說,就會有比較明顯的差別了
所以,對重量級對象應用餓漢模式,類加載時速度慢,但運行時速度快;懶漢模式則與之相反,類加載時速度快,但運行時第一次獲得對象的速度慢
(2)從用戶體驗的角度來說,我們應該首選餓漢模式。我們願意等待某個程序花較長的時間初始化,卻不喜歡在程序運行時等待太久,給人一種反應遲鈍的感覺,所以對於有重量級對象參與的單例模式,筆者推薦使用餓漢模式
2.JAVA設計模式 - 享元設計模式
在JAVA語言中,String類型就是使用了享元模式。String對象是final類型,對象一旦創建就不可改變
在JAVA中字符串常量都是存在常量池中的,JAVA會確保一個字符串常量在常量池中只有一個拷貝
String a=”abc”,其中”abc”就是一個字符串常量
享元模式代碼:1.public class Test { 2. public static void main(String[] args) { 3. String a = "abc"; 4. String b = "abc"; 5. System.out.println(a == b); 6. } 7.}上面的例子中結果爲:true ,這就說明a和b兩個引用都指向了常量池中的同一個字符串常量”abc”
這樣的設計避免了在創建N多相同對象時所產生的不必要的大量的資源消耗
3.JAVA設計模式 - 簡單工廠設計模式
簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例
簡單工廠模式是工廠模式家族中最簡單實用的模式,工廠模式就是用來生成對象的
簡單工廠設計模式作用:降低耦合簡單工廠模式代碼:
1.//手機標準 2.interface ICellPhone { 3. void sendMsg(); 4.} 5./* 小米手機 */ 6.class Millet implements ICellPhone { 7. public void sendMsg() { 8. } 9.} 10./* 華爲手機 */ 11.class Huawei implements ICellPhone { 12. public void sendMsg() { 13. } 14.} 15./* 手機工廠 */ 16.class Factory { 17. public static ICellPhone getInstance(String type){ 18. ICellPhone phone = null; 19. if("millet".equalsIgnoreCase(type)){ 20. phone = new Millet(); 21. }else if("huawei".equalsIgnoreCase(type)){ 22. phone = new Huawei(); 23. } 24. return phone; 25. } 26.} 27.public class FactoryDemo { 28. public static void main(String[] args) { 29. ICellPhone p = Factory.getInstance("millet"); 30. } 31.}如果直接使用了被調用者對象,而且又有可能會變化,那這個代碼的可擴展性和柔韌性就不是很強基於這樣的問題,所有我們就提出把客戶端(調用者)不直接跟要調用的對象產生依賴關係,這樣在擴展性和柔韌性會好一些,加入中間人,來引入工廠模式調控,單獨聲明一個工廠類,屬於被調用者這一邊,簡單工廠類只負責產生對象
4.JAVA設計模式 - 抽象工廠設計模式
抽象工廠模式與簡單工廠模式的區別:
(1)抽象工廠模式是簡單工廠方法模式的升級版本,它用來創建一組相關或者相互依賴的對象。它與簡單工廠方法模式的區別就在於,簡單工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則是針對的多個產品等級結構
(2)在編程中,通常一個產品結構,表現爲一個接口或者抽象類,也就是說,簡單工廠方法模式提供的所有產品都是衍生自同一個2接口或抽象類,而抽象工廠模式所提供的產品則是衍生自不同的接口或抽象類
(3)在抽象工廠模式中,有一個產品族的概念:所謂的產品族,是指位於不同產品等級結構中功能相關聯的產品組成的家族。抽象工廠模式所提供的一系列產品就組成一個產品族;而工廠方法提供的一系列產品稱爲一個等級結構抽象工廠模式代碼:
1.interface IProduct1 { 2. public void show(); 3. } 4. interface IProduct2 { 5. public void show(); 6. } 7. class Product1 implements IProduct1 { 8. public void show() { 9. System.out.println("這是1型產品"); 10. } 11. } 12. class Product2 implements IProduct2 { 13. public void show() { 14. System.out.println("這是2型產品"); 15. } 16. } 17. interface IFactory { 18. public IProduct1 createProduct1(); 19. public IProduct2 createProduct2(); 20. } 21. class Factory implements IFactory{ 22. public IProduct1 createProduct1() { 23. return new Product1(); 24. } 25. public IProduct2 createProduct2() { 26. return new Product2(); 27. } 28. } 29. public class Client { 30. public static void main(String[] args){ 31. IFactory factory = new Factory(); 32. factory.createProduct1().show(); 33. factory.createProduct2().show(); 34. } 35. }抽象工廠模式的優點:
抽象工廠模式除了具有簡單工廠方法模式的優點外,最主要的優點就是可以在類的內部對產品族進行約束,所謂的產品族,一般或多或少的都存在一定的關聯,抽象工廠模式就可以在類內部對產品族的關聯關係進行定義和描述,而不必專門引入一個新的類來進行管理
抽象工廠模式的缺點:
產品族的擴展將是一件十分費力的事情,假如產品族中需要增加一個新的產品,則幾乎所有的工廠類都需要進行修改,所以使用抽象工廠模式時,對產品等級結構的劃分是非常重要的
適用場景
當需要創建的對象是一系列相互關聯或相互依賴的產品族時,便可以使用抽象工廠模式,說的更明白一點,就是一個繼承體系中,如果存在着多個等級結構(即存在着多個抽象類),並且分屬各個等級結構中的實現類之間存在着一定的關聯或者約束,就可以使用抽象工廠模式,假如各個等級結構中的實現類之間不存在關聯或約束,則使用多個獨立的工廠來對產品進行創建,則更合適一點
5.JAVA設計模式 - 裝飾設計模式
裝飾模式在不鏈接其結構的情況下向現有對象添加新功能,它是一種結構型模式,因爲它充當現有類的包裝器
裝飾模式創建一個裝飾器類來包裝原始類並提供其他功能
裝飾模式代碼:1.interface Printer { 2. void print(); 3.} 4. 5.class PaperPrinter implements Printer { 6. @Override 7. public void print() { 8. System.out.println("Paper Printer"); 9. } 10.} 11. 12.class PlasticPrinter implements Printer { 13. @Override 14. public void print() { 15. System.out.println("Plastic Printer"); 16. } 17.} 18. 19.abstract class PrinterDecorator implements Printer { 20. protected Printer decoratedPrinter; 21. public PrinterDecorator(Printer d){ 22. this.decoratedPrinter = d; 23. } 24. public void print(){ 25. decoratedPrinter.print(); 26. } 27.} 28. 29.class Printer3D extends PrinterDecorator { 30. public Printer3D(Printer decoratedShape) { 31. super(decoratedShape); 32. } 33. @Override 34. public void print() { 35. System.out.println("3D."); 36. decoratedPrinter.print(); 37. } 38.} 39. 40.public class Main { 41. public static void main(String[] args) { 42. Printer plasticPrinter = new PlasticPrinter(); 43. Printer plastic3DPrinter = new Printer3D(new PlasticPrinter()); 44. Printer paper3DPrinter = new Printer3D(new PaperPrinter()); 45. plasticPrinter.print(); 46. plastic3DPrinter.print(); 47. paper3DPrinter.print(); 48. } 49.}
6.JAVA設計模式 - 觀察者設計模式
定義對象間一種一對多的依賴關係,使得當每一個對象改變狀態,則所有依賴於它的對象都會得到通知並自動更新
觀察者模式中,包括以下四個角色:
(1)被觀察者:類中有一個用來存放觀察者對象的Vector容器(之所以使用Vector而不使用List,是因爲多線程操作時,Vector在是安全的,而List則是不安全的),這個Vector容器是被觀察者類的核心,另外還有三個方法:attach方法是向這個容器中添加觀察者對象;detach方法是從容器中移除觀察者對象;notify方法是依次調用觀察者對象的對應方法。這個角色可以是接口,也可以是抽象類或者具體的類,因爲很多情況下會與其他的模式混用,所以使用抽象類的情況比較多
(2)觀察者:觀察者角色一般是一個接口,它只有一個update方法,在被觀察者狀態發生變化時,這個方法就會被觸發調用
(3)具體的被觀察者:使用這個角色是爲了便於擴展,可以在此角色中定義具體的業務邏輯
(4)具體的觀察者:觀察者接口的具體實現,在這個角色中,將定義被觀察者對象狀態發生變化時所要處理的邏輯觀察者模式代碼實現:
1.abstract class Subject { 2. private Vector obs = new Vector(); 3. public void addObserver(Observer obs){ 4. this.obs.add(obs); 5. } 6. public void delObserver(Observer obs){ 7. this.obs.remove(obs); 8. } 9. protected void notifyObserver(){ 10. for(Observer o: obs){ 11. o.update(); 12. } 13. } 14. public abstract void doSomething(); 15. } 16. class ConcreteSubject extends Subject { 17. public void doSomething(){ 18. System.out.println("被觀察者事件"); 19. this.notifyObserver(); 20. } 21. } 22. interface Observer { 23. public void update(); 24. } 25. class ConcreteObserver1 implements Observer { 26. public void update() { 27. System.out.println("觀察者1收到信息進行處理"); 28. } 29. } 30. class ConcreteObserver2 implements Observer { 31. public void update() { 32. System.out.println("觀察者2收到信息進行處理"); 33. } 34. } 35. public class Client { 36. public static void main(String[] args){ 37. Subject sub = new ConcreteSubject(); 38. sub.addObserver(new ConcreteObserver1()); //添加觀察者1 39. sub.addObserver(new ConcreteObserver2()); //添加觀察者2 40. sub.doSomething(); 41. } 42. }觀察者模式的優點:觀察者與被觀察者之間是屬於輕度的關聯關係,並且是抽象耦合的,這樣對於兩者來說都比較容易進行擴展,觀察者模式是一種常用的觸發機制,它形成一條觸發鏈,依次對各個觀察者的方法進行處理,但同時,這也算是觀察者模式一個缺點,由於是鏈式觸發,當觀察者比較多的時候,性能問題是比較令人擔憂的。並且,在鏈式結構中,比較容易出現循環引用的錯誤,造成系統假死
7.JAVA設計模式 - 適配器設計模式
在JAVA設計模式中,適配器模式作爲兩個不兼容接口之間的橋樑
通過使用適配器模式,可以統一兩個不兼容的接口
適配器設計模式代碼:1.//適配器模式 2.public class Shipeiqi{ 3. public static void main(String [] args){ 4. ModificationWindow i = new ModificationWindow(); 5. i.close(); 6. } 7.} 8. 9.//定義一個接口 10.interface IWindow{ 11. void man();//只聲明方法, 12. void min();//只聲明方法 13. void close();//只聲明方法, 14.} 15. 16.//定義一個抽象實現 17.abstract class MyWindow implements IWindow{ 18. public void man(){}; 19. public void min(){}; 20. public void close(){}; 21.} 22. 23.//定義一個類繼承接口 24.class ModificationWindow extends MyWindow{ 25. public void close(){ 26. System.out.println("我將實現關閉功能"); 27. } 28.}
8.JAVA設計模式 - 靜態代理設計模式
靜態代理設計模式:如生活當中的代理,代駕,代購,待產…
代理模式(Proxy):爲其他對象提供一種代理以控制對這個對象的訪問
代理模式,白了就是“真實對象”的代表,在訪問對象時引入一定程度的間接性,因爲這種間接性可能附和多種用途(權限的控制、對象的訪問、遠程的代理)
代理類第一先要實現接口,第二要維護一個代理的對象,代理對象也是通過主題接口聲明的,再通過構造方法或者get,set傳值,這就是靜態代理靜態代理模式代碼:
1.//靜態代理設計模式 2. 3.//聲明一個類 4.public class Jingtai{ 5. //主方法 6. public static void main(String [] args){ 7. Person p = new Person("老王 ");//實例化Person對象 8. //創建代理對象,並把被代理對象傳進來,p傳給了Matchmaker類中的target 9. Matchmaker m = new Matchmaker(p); //需要一個代理對象,把被代理對象傳過來 10. m.miai();//真正執行調用的是 11. } 12.} 13. 14.//定義一個接口 主題接口 15.interface Subject{ 16. public void miai();//抽象方法 17.} 18. 19.//代理相當於代理接口的方法 20.//定義實現一個接口,相當於被代理類 21.class Person implements Subject{ 22. private String name;//定義私有的屬性 23. //一參構造方法 24. public Person(String name){ 25. this.name = name; 26. } 27. 28. //定義實現方法 29. public void miai(){ 30. System.out.println(name+"正在相親中...");//輸出 31. } 32.} 33. 34.//定義一個代理類,代理的是過程,實現以後,是爲了實現方法,需要重寫方法 35.class Matchmaker implements Subject{ 36. private Subject target;//要代理的目標對象,通過定義一個代理的對象實現的接口,代理一個對象或者說一個屬性, 37. //可以用構造方法傳值,也可以用get,set方法傳值 38. //構造方法傳值 39. public Matchmaker(Subject target){ 40. this.target = target; 41. } 42. 43. //相親之前要做的事情,封裝起來 44. private void before(){ 45. System.out.println("爲代理人,匹配如意郎君"); 46. } 47. 48. //相親之後要做的事情 49. private void after(){ 50. System.out.println("本次相親結束."); 51. } 52. 53. //需要重寫方法,相親的方法 54. public void miai(){ 55. before();//調用相親之前要做的事情 56. target.miai();//真正執行相親的方法,調用需要目標對象 57. after();//相親之後要做的事情 58. } 59.}
9.JAVA設計模式 - 迭代器設計模式
迭代器模式以順序方式訪問集合對象的元素
迭代器模式代碼:1.interface Iterator { 2. public boolean hasNext(); 3. public Object next(); 4.} 5.class LetterBag { 6. public String names[] = {"R" , "J" ,"A" , "L"}; 7. public Iterator getIterator() { 8. return new NameIterator(); 9. } 10. class NameIterator implements Iterator { 11. int index; 12. @Override 13. public boolean hasNext() { 14. if(index < names.length){ 15. return true; 16. } 17. return false; 18. } 19. @Override 20. public Object next() { 21. if(this.hasNext()){ 22. return names[index++]; 23. } 24. return null; 25. } 26. } 27.} 28.public class Main { 29. public static void main(String[] args) { 30. LetterBag bag = new LetterBag(); 31. for(Iterator iter = bag.getIterator(); iter.hasNext();){ 32. String name = (String)iter.next(); 33. System.out.println("Name : " + name); 34. } 35. } 36.}迭代器模式的優點:
簡化了遍歷方式,對於對象集合的遍歷,還是比較麻煩的,對於數組或者有序列表,可以通過下座標來取得,但用戶需要在對集合瞭解很清楚的前提下,自行遍歷對象,但是對於hash表來說,用戶遍歷起來就比較麻煩了,而引入了迭代器方法後,用戶用起來就簡單的多了,可以提供多種遍歷方式,比如說對有序列表,我們可以根據需要提供正序遍歷,倒序遍歷兩種迭代器,用戶用起來只需要得到我們實現好的迭代器,就可以方便的對集合進行遍歷了,封裝性良好,用戶只需要得到迭代器就可以遍歷,而對於遍歷算法則不用去關心
迭代器模式的缺點:
對於比較簡單的遍歷(像數組或者有序列表),使用迭代器方式遍歷較爲繁瑣,像ArrayList,寧可願意使用for循環和get方法來遍歷集合
10.JAVA設計模式 - 生產者與消費者設計模式
生產者與消費者模式代碼:
1.package cn.sc; 2./** 3.*生產者與消費者應用案例 4.*sleep與wait區別 5.*sleep讓當前的線程進入休眠狀態,讓出cpu,讓其他線程執行 6.*如果用同步的話,有對象鎖的時候,是不會釋放的,只能等待此線程使用完,纔可以使用 7.*wait會釋放對象鎖,必須等待其他線程喚醒 8.*@author JEEP-711 9.* 10.*/ 11.public class ScXf { 12. public static void main(String[] args) { 13. Phones p = new Phones(null, null);//創建Phones對象 14. PhoneSc s = new PhoneSc(p);//創建PhoneSc對象 15. PhoneXf x = new PhoneXf(p);//創建PhoneXf對象 16. new Thread(s).start();//啓動生產者線程 17. new Thread(x).start();//啓動消費者線程 18. } 19.} 20. 21./** 22.* 手機生產者,單獨的生產者,實現Runnable接口 23.* @author JEEP-711 24.* 25.*/ 26.class PhoneSc implements Runnable{ 27. private Phones phones; 28. public PhoneSc(Phones phones){ 29. this.phones = phones; 30. } 31. @Override 32. public void run() { 33. //不斷地生產20份,生產的過程 34. for (int i = 0; i < 50; i++) { 35. if(i%2==0){ 36. phones.set("金立手機", "金立手機,中國造!"); 37. }else{ 38. phones.set("小米手機", "小米手機,爲發燒而生!"); 39. } 40. } 41. } 42.} 43. 44./** 45. *手機消費者,顧客 46.*@author JEEP-711 47.* 48.*/ 49.class PhoneXf implements Runnable{ 50. private Phones phones; 51. public PhoneXf(Phones phones){ 52. this.phones = phones; 53. } 54. @Override 55. public void run() { 56. for (int i = 0; i < 50; i++) { 57. phones.get();//調用消費產品方法 58. } 59. } 60.} 61. 62./** 63.*產品的對象,生產的手機 64.*@author JEEP-711 65.* 66.*/ 67.class Phones{ 68. @Override 69. public String toString() { 70. return "Phones [name=" + name + ", content=" + content + "]"; 71. } 72. private String name; 73. private String content; 74. /**true表示可以生產,false表示可以消費 75.*作爲標記,如何flag等於true表示可以生產,如何flag等於false表示不可生產 76.*如果flag等於false表示可以消費狀態,可以取走,flag等於true表示不能取走 77.*解決重複值得問題 78.*/ 79. private boolean flag = true;//表示可以生產,false表示可以消費 80. //構造方法 81. public Phones(String name, String content) { 82. super(); 83. this.name = name; 84. this.content = content; 85. } 86. //取得名稱方法 87. public String getName() { 88. return name; 89. } 90. //設置名稱方法 91. public void setName(String name) { 92. this.name = name; 93. } 94. //取得內容方法 95. public String getContent() { 96. return content; 97. } 98. //設置內容方法 99. public void setContent(String content) { 100. this.content = content; 101. } 102. 103. /** 104. *通過同步,解決了取值錯誤問題 105. *@param name 106. *@param content 107. */ 108. //生產製造同步方法 109. public synchronized void set(String name, String content){ 110. if(!flag){ 111. try { 112. //調用該方法,當前線程進入等待池等待狀態,沒有指定時間, 113. //需要其他線程喚醒,釋放對象鎖,讓出cpu 114. this.wait(); 115. } catch (InterruptedException e) { 116. e.printStackTrace(); 117. } 118. } 119. this.setName(name); 120. try { 121. Thread.sleep(300); 122. } catch (InterruptedException e) { 123. e.printStackTrace(); 124. } 125. this.setContent(content); 126. flag = false;//表示可以消費,取走 127. this.notify();//喚醒在該監視器上的一個線程 128. } 129. 130. //消費產品同步取值方法 131. public synchronized void get(){ 132. if(flag){ 133. try { 134. //調用該方法,當前線程進入等待池等待狀態,沒有指定時間, 135. //需要其他線程喚醒,釋放對象鎖,讓出cpu 136. this.wait(); 137. } catch (InterruptedException e) { 138. e.printStackTrace(); 139. } 140. } 141. try { 142. Thread.sleep(300); 143. } catch (InterruptedException e) { 144. e.printStackTrace(); 145. } 146. System.out.println(this.getName()+":"+this.getContent()); 147. flag = true; 148. this.notify(); 149. } 150.}
初學(面向對象近階段) Ⅳ 難點: ★★★★★★★
希望每一篇文章都能夠對讀者們提供幫助與提升,這乃是每一位筆者的初衷
感謝您的閱讀 歡迎您的留言與建議
新浪官方微博: @中國熱點影視傳媒
Blog Garden:http://www.cnblogs.com/JEEP711/
W3C/Blog:http://www.w3cschool.cn/jeep711blog/
CSDN/Blog:http://blog.csdn.net/jeep911
51CTO/Blog:http://jeep711.blog.51cto.com/