Java设计模式的一些积累
一、創建型模式
對象創建,實例化形式
1. 單例模式
單例模式說是簡單,卻又不簡單。單說單例模式的創建方式,就有7種。
單例模式供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
應用實例:
- 1、一個班級只有一個班主任。
- 2、Windows 是多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。
- 3、一些設備管理器常常設計為單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。
優點:
- 1、在內存里只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷毀實例(比如管理學院首頁頁面緩存)。
- 2、避免對資源的多重占用(比如寫文件操作)。
1)餓漢模式(多線程安全)
餓漢模式,所謂餓漢就是在類裝載的時候就創建,不管你用不用,先創建了再說,如果一直沒有被使用,便浪費了空間,典型的空間換時間,每次調用的時候,就不需要再判斷,節省了運行時間。
class Singleton {private static Singleton instance = new Singleton(); // 實例化對象//讓構造函數為 private,這樣該類就不會被實例化 private Singleton(){}// 創建唯一的對象public static Singleton getInstance(){return instance;}public void show(){System.out.println("Singleton show---");} }public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.show();} }「餓漢式」是最簡單的實現方式,這種實現方式適合那些在初始化時就要用到單例的情況,這種方式簡單粗暴,如果單例對象初始化非常快,而且占用內存非常小的時候這種方式是比較合適的,可以直接在應用啟動時加載并初始化。
2)懶漢模式(線程不安全)
懶漢模式申明了一個靜態對象,在用戶第一次調用時初始化,雖然節約了資源,但第一次加載時需要實例化,反映稍慢一些,而且在多線程不能正常工作。在多線程訪問的時候,很可能會造成多次實例化,就不再是單例了。
class Singleton {private static Singleton instance;private Singleton(){}public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}public void show(){System.out.println("Singleton show---");} }public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.show();} }3)懶漢模式(線程安全)
對創建對象的加鎖,適用于多線程中,但是效率不高
因為這種方式在getInstance()方法上加了同步鎖,所以在多線程情況下會造成線程阻塞,把大量的線程鎖在外面,只有一個線程執行完畢才會執行下一個線程。
class Singleton {private static Singleton instance;private Singleton(){}public static synchronized Singleton getInstance(){ // 對創建對象加速if(instance == null){instance = new Singleton();}return instance;}public void show(){System.out.println("Singleton show---");} }public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.show();} }4)雙重校驗鎖 - DCL(線程安全)【推薦使用】
懶漢式(線程安全)」毫無疑問存在性能的問題 —?如果存在很多次getInstance()的調用,那性能問題就不得不考慮了
為什么雙重校驗鎖可以保證線程安全,原因就是檢測null的操作和創建對象的操作分離了。如果這兩個操作能夠原子地進行,那么單例就已經保證了。
class Singleton {private volatile static Singleton instance;private Singleton(){}public static synchronized Singleton getInstance(){ // 對創建對象加速if(instance == null){synchronized(Singleton.class){if (instance == null){instance = new Singleton();}}}return instance;}public void show(){System.out.println("Singleton show---");} }public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.show();} } 被volatile修飾的變量的值,將不會被本地線程緩存, 所有對該變量的讀寫都是直接操作共享內存,從而確保多個線程能正確的處理該變量。為什么要使用volatile修飾?
雖然已經使用synchronized進行同步,但在第4步創建對象時,會有下面的偽代碼:
memory=allocate(); //1:分配內存空間 ctorInstance(); //2:初始化對象 singleton=memory; //3:設置singleton指向剛排序的內存空間 復制代碼當線程A在執行上面偽代碼時,2和3可能會發生重排序,因為重排序并不影響運行結果,還可以提升性能,所以JVM是允許的。如果此時偽代碼發生重排序,步驟變為1->3->2,線程A執行到第3步時,線程B調用getsingleton方法,在判斷singleton==null時不為null,則返回singleton。但此時singleton并還沒初始化完畢,線程B訪問的將是個還沒初始化完畢的對象。當聲明對象的引用為volatile后,偽代碼的2、3的重排序在多線程中將被禁止!
來源于--掘金
「雙重校驗鎖」:既可以達到線程安全,也可以使性能不受很大的影響,換句話說在保證線程安全的前提下,既節省空間也節省了時間,集合了「餓漢式」和兩種「懶漢式」的優點,取其精華,去其槽粕。
對于volatile關鍵字,還是存在很多爭議的。由于volatile關鍵字可能會屏蔽掉虛擬機中一些必要的代碼優化,所以運行效率并不是很高。也就是說,雖然可以使用“雙重檢查加鎖”機制來實現線程安全的單例,但并不建議大量采用,可以根據情況來選用。
5)靜態內部類(線程安全)【推薦使用】
在很多情況下JVM已經為我們提供了同步控制,比如:
- 在static {...}區塊中初始化的數據
- 訪問final字段時
在JVM進行類加載的時候他會保證數據是同步的,我們可以這樣實現:采用內部類,在這個內部類里面去創建對象實例。這樣的話,只要應用中不使用內部類 JVM 就不會去加載這個單例類,也就不會創建單例對象,從而實現「懶漢式」的延遲加載和線程安全。
class Singleton {private Singleton(){}public static Singleton getInstance(){ return Inner.instance;}// 內部靜態類private static class Inner{private final static Singleton instance = new Singleton();}public void show(){System.out.println("Singleton show---");} }public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.show();} }內部類的實現方法有以下優點:
6)使用枚舉
枚舉單例的優點就是簡單,但是大部分應用開發很少用枚舉,可讀性并不是很高,不建議用。
enum Singleton {INSTANCE;public void show(){System.out.println("Singleton show---");} }public class Main {public static void main(String[] args) {Singleton instance = Singleton.INSTANCE;instance.show();} }7)使用容器
在使用時根據key獲取對象對應類型的對象。這種方式使得我們可以管理多種類型的單例,并且在使用時可以通過統一的接口進行獲取操作,降低了用戶的使用成本,也對用戶隱藏了具體實現,降低了耦合度。
public class SingletonManager { private static Map<String, Object> objMap = new HashMap<String,Object>();private Singleton() { }public static void registerService(String key, Objectinstance) {if (!objMap.containsKey(key) ) {objMap.put(key, instance) ;}}public static Object getService(String key) {return objMap.get(key) ;} }?
2. 抽象工廠模式
代碼實現:
//實現一個抽象工廠interface IFactory{public Reader getReader(); }class JPGFac implements IFactory{public Reader getReader(){return new JPGReader();} }class GIFFac implements IFactory{ // 使用工廠創建對應的類public Reader getReader(){return new GIFReader();} }interface Reader{public void show(); }class JPGReader implements Reader{public void show(){System.out.println("this is jpg");} }class GIFReader implements Reader{public void show(){System.out.println("this is gif");} }public class Main {public static void main(String[] args) {System.out.println("Hello W3Cschool!");IFactory gifFac = new GIFFac();gifFac.getReader().show();} }?
?
二、結構型模式
類和對象結合形成更大的結構
1. 適配器模式
讓不兼容的類、接口可以合作適配。(增加Adapter 適配類)
類的適配器:
有一個Source類,擁有一個方法,待適配,目標接口是Targetable,通過Adapter類,將Source的功能擴展到Targetable里
class Source{public void method1(){System.out.println("this is method1");} }interface ITargetable{ // 需要適配的接口public void method1();public void method2(); }class Adapter extends Source implements ITargetable{ // 自定義一個適配器public void method2(){System.out.println("this is method2");} }public class Main {public static void main(String[] args) {ITargetable target = new Adapter(); // 將 Source 類的方法適配到了接口ITargetable上target.method1();target.method2();} }?
2. 橋接模式
抽象與實現解耦,使二者可以獨立變化
把事物和其具體實現分開,使他們可以各自獨立的變化。
像我們常用的JDBC橋DriverManager一樣,JDBC進行連接數據庫的時候,在各個數據庫之間進行切換,基本不需要動太多的代碼,甚至絲毫不用動,原因就是JDBC提供統一接口,每個數據庫提供各自的實現,用一個叫做數據庫驅動的程序來橋接就行了
interface ISQLManager{public void show(); }class Mysql implements ISQLManager{public void show(){System.out.println("this is Mysql");} }class Oracle implements ISQLManager{public void show(){System.out.println("this is oracle");} }// -------- 這里可一增加多個數據庫的實現 ------- abstract class Bridge{private ISQLManager manager;public void setManager(ISQLManager manager){this.manager = manager;}public ISQLManager getManager(){return this.manager;}public abstract void show();}class MyBridge extends Bridge{public void show(){getManager().show();} }public class Main {public static void main(String[] args) {ISQLManager mysql = new Mysql();Bridge myBridge = new MyBridge();myBridge.setManager(mysql);myBridge.show();} }3. 裝飾者模式
為類對象增加功能
Source類是被裝飾類,Decorator類是一個裝飾類,可以為Source類動態的添加一些功能
interface IModify{public void method(); }class BeModified implements IModify{ // 需要被裝飾的類,即需要擴展的功能public void method(){System.out.println("this is orignal method");} }class Decorator implements IModify{ // 擴展功能的類private IModify modify;public Decorator(IModify modify){this.modify = modify;}public void method(){System.out.println("before method----");modify.method();System.out.println("afther method -------");}}public class Main {public static void main(String[] args) {IModify modify = new BeModified();IModify target = new Decorator(modify);target.method(); // 擴展增加了功能} }4. 外觀模式
外觀模式是為了解決類與類之家的依賴關系的,就是將他們的關系放在一個Facade類中,降低了類類之間的耦合度
比如計算機主機中,只需要按下主機的開機按鈕(on()),就可以調用其他硬件設備和軟件的啟動方法,如內存(Memory)的開啟(check()),CPU的運行(run()),硬盤(Harddisk)的讀取(read())。
計算機關機,則按下關機按鈕(off),依次關閉
class Momery{public void check(){System.out.println("momery is checking ---");}public void shutDown(){System.out.println("momery shutdown");} }class CPU{public void run(){System.out.println("cpu is running ---");}public void shutDown(){System.out.println("cpu shutdown");} }class Harddisk{public void read(){System.out.println("Harddisk is reading ---");}public void shutDown(){System.out.println("Harddisk shutdown");} }class Computer{private Momery momery;private CPU cpu;private Harddisk harddisk;public Computer(){momery = new Momery();cpu = new CPU();harddisk = new Harddisk();}public void start(){momery.check();cpu.run();harddisk.read();}public void shutDown(){momery.shutDown();cpu.shutDown();harddisk.shutDown();}}public class Main {public static void main(String[] args) {Computer computer = new Computer();computer.start();computer.shutDown();} }5. 組合模式
組合模式讓客戶以一致性的方式處理個別對象或者組合
使用組合模式,設計一個殺毒軟件框架,該軟件既可以對某個文件夾Folder殺毒,也可以對某個指定的文件殺毒,文件種類包括,文本文檔TextFile,圖片文件ImageFile,視頻文件VideoFile,繪制類圖并編程模擬實現。
import java.util.ArrayList;interface IKillVirus{public void killVirus(); }class TextFile implements IKillVirus{private String name;public TextFile(String name){this.name = name;}public void killVirus(){System.out.println("name : " + name +" killing virus-- ");} }class ImageFile implements IKillVirus{private String name;public ImageFile(String name){this.name = name;}public void killVirus(){System.out.println("name : " + name +" killing virus-- ");} }class Folder implements IKillVirus{private ArrayList<IKillVirus> array;private String name;public Folder(String name){this.name = name;array = new ArrayList<IKillVirus>();}public void add(IKillVirus kv){array.add(kv);}public void remove(IKillVirus kv){array.remove(kv);}public int getFolder(int index){return array.get(index);}public void killVirus(){System.out.println("name : " + name +" killing virus-- ");for (int kv: array){kv.killVirus();}} }public class Main {public static void main(String[] args) {IKillVirus textFile = new TextFile("textFile");IKillVirus imageFile = new ImageFile("imageFile");IKillVirus folder = new Folder("folder");folder.add(textFile);folder.add(imageFile);folder.killVirus();} }6. 代理模式
代理模式就是多一個代理類出來,替原對象進行一些操作,比如我們在租房子的時候回去找中介,為什么呢?因為你對該地區房屋的信息掌握的不夠全面,希望找一個更熟悉的人去幫你做,此處的代理就是這個意思。再如我們有的時候打官司,我們需要請律師,因為律師在法律方面有專長,可以替我們進行操作,表達我們的想法。
interface IService{public void method(); }class ServiceObject implements IService{public void method(){System.out.println("this is ServiceObject method----");} }class Proxy implements IService{private ServiceObject sobject;public Proxy(){this.sobject = new ServiceObject();}public void before(){System.out.println("before method --");}public void afther(){System.out.println("afther method ----");}public void method(){before();this.sobject.method();afther();}}public class Main {public static void main(String[] args) {IService pro = new Proxy();pro.method();} }?
三、行為型模式
類與對象之間如何交互,關心他們之間的通訊方式
?
1. 命令模式
?實現命令發出者和執行者解耦
命令模式很好理解,舉個例子,司令員下令讓士兵去干件事情,從整個事情的角度來考慮,司令員的作用是,發出口令,口令經過傳遞,傳到了士兵耳朵里,士兵去執行。這個過程好在,三者相互解耦,任何一方都不用去依賴其他人,只需要做好自己的事兒就行,司令員要的是結果,不會去關注到底士兵是怎么實現的。
interface ICommand{public void command(); }class MyCommand implements ICommand{private Sodier sd;public MyCommand(Sodier sd){this.sd = sd;}public void command(){ // 命令士兵做出對應動作sd.action();} }class Sodier{public void action(){System.out.println("sodier get command----");} }class Boss{private ICommand commander;public Boss(ICommand commander){ // boss 指示命令this.commander = commander;}public void order(){this.commander.command();} }public class Main {public static void main(String[] args) {ICommand com = new MyCommand(new Sodier());Boss boss = new Boss(com);boss.order();} }?
2. 觀察者模式
對象一對多依賴,當一個對象改變的時候,它的對象會收到通知
某公司欲開發一套機房監控系統,如果機房達到一定指定溫度,傳感器將作出反應,將信號傳遞給響應設備,如警示燈將閃爍,報警器將發出警報,安全逃生門將自動開啟、隔熱門將自動關閉,每一響應設備的行為右專門的程序來控制,為支持將來引入新類型的響應設備,用觀察者模式設計該系統.
import java.util.ArrayList;interface IAlarmListener{public void doSomthing(); }class Voice implements IAlarmListener{public void doSomthing(){System.out.println("voice alarm ---------- ");} }class Light implements IAlarmListener{public void doSomthing(){System.out.println("light red ---------- ");} }class Sercurity implements IAlarmListener{public void doSomthing(){System.out.println("Sercurity ---------- ");} }class Sensor{private ArrayList<IAlarmListener> listener = new ArrayList();private int value;public void add(IAlarmListener al){listener.add(al);}public void delete(IAlarmListener al){listener.remove(al);}public void setValue(int value){this.value = value;}public void sendMessage(){if (this.value > 50){for(IAlarmListener al: listener){al.doSomthing();}}else{System.out.println("normal operation ---- ");}} }public class Main {public static void main(String[] args) {IAlarmListener light = new Light();IAlarmListener voice = new Voice();IAlarmListener ser = new Sercurity();Sensor sen = new Sensor();sen.add(light);sen.add(voice);sen.add(ser);sen.setValue(60);sen.sendMessage();sen.setValue(20);sen.sendMessage();} }3.模板模式
?
4. 狀態模式
當對象的狀態改變時,同時改變其行為
就拿QQ來說,有幾種狀態,在線、隱身、忙碌等,每個狀態對應不同的操作,而且你的好友也能看到你的狀態,所以,狀態模式就兩點:1、可以通過改變狀態來獲得不同的行為。2、你的好友能同時看到你的變化。
class State{private String value;public void setValue(String value){this.value = value;}public String getValue(){return this.value;}public void method1(){System.out.println("this is method1 -------");}public void method2(){System.out.println("this is mthod2 ------");}}class Context{private State state;public void setState(State state){this.state = state;}public void method(){if (this.state.getValue().equals("state1")){this.state.method1();}else{this.state.method2();}}}public class Main {public static void main(String[] args) {State state = new State();state.setValue("state1");Context context = new Context();context.setState(state);context.method();} }?
5. 中介者模式
降低類與類之間的耦合
中介者模式也是用來降低類類之間的耦合的,因為如果類類之間有依賴關系的話,不利于功能的拓展和維護,因為只要修改一個對象,其它關聯的對象都得進行修改。如果使用中介者模式,只需關心和Mediator類的關系,具體類類之間的關系及調度交給Mediator就行,這有點像spring容器的作用。
?
6. 迭代器模式
迭代器模式就是順序訪問聚集中的對象,一般來說,集合中非常常見,如果對集合類比較熟悉的話,理解本模式會十分輕松。這句話包含兩層意思:一是需要遍歷的對象,即聚集對象,二是迭代器對象,用于對聚集對象進行遍歷訪問。我們看下關系圖:
?
具體的角色:
(1)迭代器角色(Iterator):定義遍歷元素所需要的方法,一般來說會有這么三個方法:取得下一個元素的方法next(),判斷是否遍歷結束的方法hasNext()),移出當前對象的方法remove(),
(2)具體迭代器角色(Concrete Iterator):實現迭代器接口中定義的方法,完成集合的迭代。
(3)容器角色(Aggregate): 一般是一個接口,提供一個iterator()方法,例如java中的Collection接口,List接口,Set接口等
(4)具體容器角色(ConcreteAggregate):就是抽象容器的具體實現類,比如List接口的有序列表實現ArrayList,List接口的鏈表實現LinkList,Set接口的哈希列表的實現HashSet等。
具體實現的代碼:
package com.java.xlmdemo;import java.util.ArrayList; import java.util.List;// 測試迭代器的使用 public class IteratorTest {public static void main(String[] args) {System.out.println("this is just for iterator test");Collections ct = new ConcreteCollections();ct.add(10);ct.add(20);ct.add(30);// 使用for 循環來迭代 // for (Iterator iterator = ct.iterator(); iterator.hasNext();){ // int value = (int)iterator.next(); // System.out.println(value); // }Iterator iterator = ct.iterator();while(iterator.hasNext()){int value = (int)iterator.next();System.out.println(value);}while (iterator.hasPrevious()){int value = (int)iterator.previous();System.out.println(value);}} }// 定義一個 迭代器 接口 interface Iterator{// 判斷是否有下一個元素public boolean hasNext();// 獲取下一個元素public Object next();public boolean hasPrevious();// 獲取前一個元素public Object previous(); }interface Collections{// 獲取一個迭代器public Iterator iterator();// 增加、刪除一個元素public void add(Object obj);public void remove(Object obj);// 獲取元素public Object get(int i);// 獲取大小public int size(); }class ConcreteIterator implements Iterator{private List list = new ArrayList();private int cursor = 0;public ConcreteIterator(List list){this.list = list;}@Overridepublic boolean hasNext() {if(cursor != list.size()){return true;}return false;}@Overridepublic Object next() {Object obj = null;if (hasNext()){obj = list.get(cursor++); // 獲取元素,并使 cursor 加 1}return obj;}@Overridepublic boolean hasPrevious() {// 必須大于 等于 1, 防止previous 先減去1 ,造成數組越界if (cursor >= 1 && list.size() > 0){return true;}return false;}@Overridepublic Object previous() {Object obj = null;if (hasPrevious()){obj = list.get(--cursor); // 如果是打印前一個元素,由于數組從0開始數起,則需要先減去 1}return obj;} }class ConcreteCollections implements Collections{private List list = new ArrayList();@Overridepublic Iterator iterator() {return new ConcreteIterator(list);}@Overridepublic void add(Object obj) {list.add(obj);}@Overridepublic void remove(Object obj) {list.remove(obj);}@Overridepublic Object get(int i) {Object obj = null;obj = list.get(i);return obj;}@Overridepublic int size() {return list.size();} }?
迭代器模式的優缺點
? ? ? ? 1.?迭代器模式的優點有:
- 簡化了遍歷方式,對于對象集合的遍歷,還是比較麻煩的,對于數組或者有序列表,我們尚可以通過游標來取得,但用戶需要在對集合了解很清楚的前提下,自行遍歷對象,但是對于hash表來說,用戶遍歷起來就比較麻煩了。而引入了迭代器方法后,用戶用起來就簡單的多了。
- 可以提供多種遍歷方式,比如說對有序列表,我們可以根據需要提供正序遍歷,倒序遍歷兩種迭代器,用戶用起來只需要得到我們實現好的迭代器,就可以方便的對集合進行遍歷了。
- 封裝性良好,用戶只需要得到迭代器就可以遍歷,而對于遍歷算法則不用去關心。
??????? 2. 迭代器模式的缺點:
- 對于比較簡單的遍歷(像數組或者有序列表),使用迭代器方式遍歷較為繁瑣,大家可能都有感覺,像ArrayList,我們寧可愿意使用for循環和get方法來遍歷集合。
?
迭代器模式的適用場景
?????? 迭代器模式是與集合共生共死的,一般來說,我們只要實現一個集合,就需要同時提供這個集合的迭代器,就像java中的Collection,List、Set、Map等,這些集合都有自己的迭代器。假如我們要實現一個這樣的新的容器,當然也需要引入迭代器模式,給我們的容器實現一個迭代器。
?????? 但是,由于容器與迭代器的關系太密切了,所以大多數語言在實現容器的時候都給提供了迭代器,并且這些語言提供的容器和迭代器在絕大多數情況下就可以滿足我們的需要,所以現在需要我們自己去實踐迭代器模式的場景還是比較少見的,我們只需要使用語言中已有的容器和迭代器就可以了。
?
?
?
?
?
總結
以上是生活随笔為你收集整理的Java设计模式的一些积累的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搭建react + typescript
- 下一篇: Conway生命游戏