單例模式:
即一個應用程序中,某個類的實例對象只有一個,你沒有辦法去new,因為構造器是被private修飾的,一般通過其get方法獲取到他們的實例。
懶漢寫法(線程不安全)
publicclassSingleton{ privatestaticSingletonsingleton; privateSingleton(){ } publicstaticSingletongetInstance(){ if(singleton==null){ singleton=newSingleton(); } returnsingleton; } }
懶漢式寫法(線程安全)
publicclassSingleton{ privatestaticSingletoninstance; privateSingleton(){} publicstaticsynchronizedSingletongetInstance(){ if(instance==null){ instance=newSingleton(); } returninstance; } }
餓漢式寫法
publicclassSingleton{ privatestaticSingletoninstance=newSingleton(); privateSingleton(){} publicstaticSingletongetInstance(){ returninstance; } }
靜態內部類
publicclassSingleton{ privatestaticclassSingletonHolder{ privatestaticfinalSingletonINSTANCE=newSingleton(); } privateSingleton(){} publicstaticfinalSingletongetInstance(){ returnSingletonHolder.INSTANCE; } }
枚舉
這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象,可謂是很堅強的壁壘啊,不過,個人認為由于1.5中才加入enum特性,用這種方式寫不免讓人感覺生疏。
publicenumSingleton{ INSTANCE; publicvoidwhateverMethod(){ } }
雙重校驗鎖
publicclassSingleton{ privatevolatilestaticSingletonsingleton; privateSingleton(){} publicstaticSingletongetSingleton(){ if(singleton==null){ synchronized(Singleton.class){ if(singleton==null){ singleton=newSingleton(); } } } returnsingleton; } }
實際應用場景:
在Spring中創建的Bean實例默認都是單例模式存在的。
Windows的Task Manager(任務管理器)就是很典型的單例模式(這個很熟悉吧),想想看,是不是呢,你能打開兩個windows task manager嗎?不信你自己試試看哦~
windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統運行過程中,回收站一直維護著僅有的一個實例。
網站的計數器,一般也是采用單例模式實現,否則難以同步。
應用程序的日志應用,一般都何用單例模式實現,這一般是由于共享的日志文件一直處于打開狀態,因為只能有一個實例去操作,否則內容不好追加。
觀察者模式:
對象間一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。
給你舉個栗子:假設有三個人,小美(女,22),小王和小李。小美很漂亮,小王和小李是兩個程序猿,時刻關注著小美的一舉一動。有一天,小美說了一句:“誰來陪我打游戲啊。”
這句話被小王和小李聽到了,結果樂壞了,蹭蹭蹭,沒一會兒,小王就沖到小美家門口了,在這里,小美是被觀察者,小王和小李是觀察者,被觀察者發出一條信息,然后觀察者們進行相應的處理,看代碼:
publicinterfacePerson{ //小王和小李通過這個接口可以接收到小美發過來的消息 voidgetMessage(Strings); }
這個接口相當于小王和小李的電話號碼,小美發送通知的時候就會撥打getMessage這個電話,撥打電話就是調用接口,看不懂沒關系,先往下看
publicclassLaoWangimplementsPerson{ privateStringname="小王"; publicLaoWang(){ } @Override publicvoidgetMessage(Strings){ System.out.println(name+"接到了小美打過來的電話,電話內容是:"+s); } } publicclassLaoLiimplementsPerson{ privateStringname="小李"; publicLaoLi(){ } @Override publicvoidgetMessage(Strings){ System.out.println(name+"接到了小美打過來的電話,電話內容是:->"+s); } }
代碼很簡單,我們再看看小美的代碼:
publicclassXiaoMei{ List
我們寫一個測試類來看一下結果對不對
publicclassTest{ publicstaticvoidmain(String[]args){ XiaoMeixiao_mei=newXiaoMei(); LaoWanglao_wang=newLaoWang(); LaoLilao_li=newLaoLi(); //小王和小李在小美那里都注冊了一下 xiao_mei.addPerson(lao_wang); xiao_mei.addPerson(lao_li); //小美向小王和小李發送通知 xiao_mei.notifyPerson(); } }
實際應用場景:
場景描述:
以購票為核心業務(此模式不限于該業務),但圍繞購票會產生不同的其他邏輯,如:
購票后記錄文本日志
購票后記錄數據庫日志
購票后發送短信
購票送抵扣卷、兌換卷、積分
-其他各類活動等
傳統解決方案:
在購票邏輯等類內部增加相關代碼,完成各種邏輯。
存在問題:
1、一旦某個業務邏輯發生改變,如購票業務中增加其他業務邏輯,需要修改購票核心文件、甚至購票流程。
2、日積月累后,文件冗長,導致后續維護困難。
存在問題原因主要是程序的"緊密耦合",使用觀察模式將目前的業務邏輯優化成"松耦合",達到易維護、易修改的目的,同時也符合面向接口編程的思想。
觀察者模式典型實現方式:
定義2個接口:觀察者(通知)接口、被觀察者(主題)接口
定義2個類,觀察者對象實現觀察者接口、主題類實現被觀者接口
主題類注冊自己需要通知的觀察者
主題類某個業務邏輯發生時通知觀察者對象,每個觀察者執行自己的業務邏輯。
裝飾者模式
對已有的業務邏輯進一步的封裝,使其增加額外的功能,如Java中的IO流就使用了裝飾者模式,用戶在使用的時候,可以任意組裝,達到自己想要的效果。
舉個栗子,我想吃三明治,首先我需要一根大大的香腸,我喜歡吃奶油,在香腸上面加一點奶油,再放一點蔬菜,最后再用兩片面包夾一下,很豐盛的一頓午飯,營養又健康。那我們應該怎么來寫代碼呢?首先,我們需要寫一個Food類,讓其他所有食物都來繼承這個類,看代碼:
publicclassFood{ privateStringfood_name; publicFood(){ } publicFood(Stringfood_name){ this.food_name=food_name; } publicStringmake(){ returnfood_name; }; }
代碼很簡單,我就不解釋了,然后我們寫幾個子類繼承它:
//面包類 publicclassBreadextendsFood{ privateFoodbasic_food; publicBread(Foodbasic_food){ this.basic_food=basic_food; } publicStringmake(){ returnbasic_food.make()+"+面包"; } } //奶油類 publicclassCreamextendsFood{ privateFoodbasic_food; publicCream(Foodbasic_food){ this.basic_food=basic_food; } publicStringmake(){ returnbasic_food.make()+"+奶油"; } } //蔬菜類 publicclassVegetableextendsFood{ privateFoodbasic_food; publicVegetable(Foodbasic_food){ this.basic_food=basic_food; } publicStringmake(){ returnbasic_food.make()+"+蔬菜"; } }
這幾個類都是差不多的,構造方法傳入一個Food類型的參數,然后在make方法中加入一些自己的邏輯,如果你還是看不懂為什么這么寫,不急,你看看我的Test類是怎么寫的,一看你就明白了
publicclassTest{ publicstaticvoidmain(String[]args){ Foodfood=newBread(newVegetable(newCream(newFood("香腸")))); System.out.println(food.make()); } }
看到沒有,一層一層封裝,我們從里往外看:最里面我new了一個香腸,在香腸的外面我包裹了一層奶油,在奶油的外面我又加了一層蔬菜,最外面我放的是面包,是不是很形象,哈哈~ 這個設計模式簡直跟現實生活中一摸一樣,看懂了嗎?
實際應用場景:
如上述一樣,不同的人,選擇的搭配不同,對應價格也不相同,若是應用傳統方式你會發現這里四種配料就要寫十幾種實現類了,那如果我們的配料是二十幾種或者三十幾種呢,那么使用繼承這種 方式肯定會使我們的子類爆炸。
通過不同的組合以Food food = new Bread(new Vegetable(new Cream(new Food("香腸"))));形式更加簡化,結構更加清楚的方式展現。
優點:
把類中的裝飾功能從類中搬除,可以簡化原來的類
可以把類的 核心職責和裝飾功能區分開來,結構清晰 明了并且可以去除相關類的重復的裝飾邏輯。
適配器模式
將兩種完全不同的事物聯系到一起,就像現實生活中的變壓器。假設一個手機充電器需要的電壓是20V,但是正常的電壓是220V,這時候就需要一個變壓器,將220V的電壓轉換成20V的電壓,這樣,變壓器就將20V的電壓和手機聯系起來了。
publicclassTest{ publicstaticvoidmain(String[]args){ Phonephone=newPhone(); VoltageAdapteradapter=newVoltageAdapter(); phone.setAdapter(adapter); phone.charge(); } } //手機類 classPhone{ publicstaticfinalintV=220;//正常電壓220v,是一個常量 privateVoltageAdapteradapter; //充電 publicvoidcharge(){ adapter.changeVoltage(); } publicvoidsetAdapter(VoltageAdapteradapter){ this.adapter=adapter; } } //變壓器 classVoltageAdapter{ //改變電壓的功能 publicvoidchangeVoltage(){ System.out.println("正在充電..."); System.out.println("原始電壓:"+Phone.V+"V"); System.out.println("經過變壓器轉換之后的電壓:"+(Phone.V-200)+"V"); } }
適配器模式應用場景
類適配器與對象適配器的使用場景一致,僅僅是實現手段稍有區別,二者主要用于如下場景:
想要使用一個已經存在的類,但是它卻不符合現有的接口規范,導致無法直接去訪問,這時創建一個適配器就能間接去訪問這個類中的方法。
我們有一個類,想將其設計為可重用的類(可被多處訪問),我們可以創建適配器來將這個類來適配其他沒有提供合適接口的類。
以上兩個場景其實就是從兩個角度來描述一類問題,那就是要訪問的方法不在合適的接口里,一個從接口出發(被訪問),一個從訪問出發(主動訪問)。
接口適配器使用場景:
想要使用接口中的某個或某些方法,但是接口中有太多方法,我們要使用時必須實現接口并實現其中的所有方法,可以使用抽象類來實現接口,并不對方法進行實現(僅置空),然后我們再繼承這個抽象類來通過重寫想用的方法的方式來實現。這個抽象類就是適配器。
工廠模式
簡單工廠模式:一個抽象的接口,多個抽象接口的實現類,一個工廠類,用來實例化抽象的接口
//抽象產品類 abstractclassCar{ publicvoidrun(); publicvoidstop(); } //具體實現類 classBenzimplementsCar{ publicvoidrun(){ System.out.println("Benz開始啟動了。。。。。"); } publicvoidstop(){ System.out.println("Benz停車了。。。。。"); } } classFordimplementsCar{ publicvoidrun(){ System.out.println("Ford開始啟動了。。。"); } publicvoidstop(){ System.out.println("Ford停車了。。。。"); } } //工廠類 classFactory{ publicstaticCargetCarInstance(Stringtype){ Carc=null; if("Benz".equals(type)){ c=newBenz(); } if("Ford".equals(type)){ c=newFord(); } returnc; } } publicclassTest{ publicstaticvoidmain(String[]args){ Carc=Factory.getCarInstance("Benz"); if(c!=null){ c.run(); c.stop(); }else{ System.out.println("造不了這種汽車。。。"); } } }
工廠方法模式:有四個角色,抽象工廠模式,具體工廠模式,抽象產品模式,具體產品模式。不再是由一個工廠類去實例化具體的產品,而是由抽象工廠的子類去實例化產品
//抽象產品角色 publicinterfaceMoveable{ voidrun(); } //具體產品角色 publicclassPlaneimplementsMoveable{ @Override publicvoidrun(){ System.out.println("plane...."); } } publicclassBroomimplementsMoveable{ @Override publicvoidrun(){ System.out.println("broom....."); } } //抽象工廠 publicabstractclassVehicleFactory{ abstractMoveablecreate(); } //具體工廠 publicclassPlaneFactoryextendsVehicleFactory{ publicMoveablecreate(){ returnnewPlane(); } } publicclassBroomFactoryextendsVehicleFactory{ publicMoveablecreate(){ returnnewBroom(); } } //測試類 publicclassTest{ publicstaticvoidmain(String[]args){ VehicleFactoryfactory=newBroomFactory(); Moveablem=factory.create(); m.run(); } }
抽象工廠模式:與工廠方法模式不同的是,工廠方法模式中的工廠只生產單一的產品,而抽象工廠模式中的工廠生產多個產品
//抽象工廠類 publicabstractclassAbstractFactory{ publicabstractVehiclecreateVehicle(); publicabstractWeaponcreateWeapon(); publicabstractFoodcreateFood(); } //具體工廠類,其中Food,Vehicle,Weapon是抽象類, publicclassDefaultFactoryextendsAbstractFactory{ @Override publicFoodcreateFood(){ returnnewApple(); } @Override publicVehiclecreateVehicle(){ returnnewCar(); } @Override publicWeaponcreateWeapon(){ returnnewAK47(); } } //測試類 publicclassTest{ publicstaticvoidmain(String[]args){ AbstractFactoryf=newDefaultFactory(); Vehiclev=f.createVehicle(); v.run(); Weaponw=f.createWeapon(); w.shoot(); Fooda=f.createFood(); a.printName(); } }
工廠模式例子很多,我大概說一下上面三種的特點:
簡單工廠模式:每次擴展時,需要添加一個類,并修改工廠類代碼,給get方法添加一條分支。
工廠方法模式:它與簡單工廠的區別就在于有多個工廠,每個工廠只專注生產一種產品,當需要修改獲取的產品時,只需要修改所訪問的工廠就行。
抽象工廠模式:每次擴展時,需要添加1個類,并添加1個對應工廠。既是優點(擴展靈活,不需要修改舊的類)又是缺點(總是要編寫新工廠)。
代理模式(proxy)
有兩種,靜態代理和動態代理。先說靜態代理,很多理論性的東西我不講,我就算講了,你們也看不懂。什么真實角色,抽象角色,代理角色,委托角色。。。亂七八糟的,我是看不懂。
之前學代理模式的時候,去網上翻一下,資料一大堆,打開鏈接一看,基本上都是給你分析有什么什么角色,理論一大堆,看起來很費勁,不信的話你們可以去看看,我是看不懂他們在說什么。咱不來虛的,直接用生活中的例子說話。
注意:我這里并不是否定理論知識,我只是覺得有時候理論知識晦澀難懂,喜歡挑刺的人一邊去,你是來學習知識的,不是來挑刺的
到了一定的年齡,我們就要結婚,結婚是一件很麻煩的事情,(包括那些被父母催婚的)。有錢的家庭可能會找司儀來主持婚禮,顯得熱鬧,洋氣~好了,現在婚慶公司的生意來了,我們只需要給錢,婚慶公司就會幫我們安排一整套結婚的流程。
整個流程大概是這樣的:家里人催婚->男女雙方家庭商定結婚的黃道即日->找一家靠譜的婚慶公司->在約定的時間舉行結婚儀式->結婚完畢
婚慶公司打算怎么安排婚禮的節目,在婚禮完畢以后婚慶公司會做什么,我們一概不知。。。別擔心,不是黑中介,我們只要把錢給人家,人家會把事情給我們做好。所以,這里的婚慶公司相當于代理角色,現在明白什么是代理角色了吧。
代碼實現請看:
//代理接口 publicinterfaceProxyInterface{ //需要代理的是結婚這件事,如果還有其他事情需要代理,比如吃飯睡覺上廁所,也可以寫 voidmarry(); //代理吃飯(自己的飯,讓別人吃去吧) //voideat(); //代理拉屎,自己的屎,讓別人拉去吧 //voidshit(); }
文明社會,代理吃飯,代理拉屎什么的我就不寫了,有傷社會風化~~~能明白就好
好了,我們看看婚慶公司的代碼:
publicclassWeddingCompanyimplementsProxyInterface{ privateProxyInterfaceproxyInterface; publicWeddingCompany(ProxyInterfaceproxyInterface){ this.proxyInterface=proxyInterface; } @Override publicvoidmarry(){ System.out.println("我們是婚慶公司的"); System.out.println("我們在做結婚前的準備工作"); System.out.println("節目彩排..."); System.out.println("禮物購買..."); System.out.println("工作人員分工..."); System.out.println("可以開始結婚了"); proxyInterface.marry(); System.out.println("結婚完畢,我們需要做后續處理,你們可以回家了,其余的事情我們公司來做"); } }
看到沒有,婚慶公司需要做的事情很多,我們再看看結婚家庭的代碼:
publicclassNormalHomeimplementsProxyInterface{ @Override publicvoidmarry(){ System.out.println("我們結婚啦~"); } }
這個已經很明顯了,結婚家庭只需要結婚,而婚慶公司要包攬一切,前前后后的事情都是婚慶公司來做,聽說現在婚慶公司很賺錢的,這就是原因,干的活多,能不賺錢嗎?
來看看測試類代碼:
publicclassTest{ publicstaticvoidmain(String[]args){ ProxyInterfaceproxyInterface=newWeddingCompany(newNormalHome()); proxyInterface.marry(); } }
運行結果如下:
這里可以看出代理模式與裝飾模式很相似,這里簡單介紹下其區別:
代理模式(Proxy 模式)可理解為:我想做,但不能做,我需要有一個能干的人來幫我做。即:代理,偏重因自己無法完成或自己無需關心,需要他人干涉事件流程,更多的是對對象的控制。
裝飾器模式(Decorator 模式)可理解為:我想做,但不能做,我需要有各類特長的人來幫我做,但我有時只需要一個人,有時又需要很多人。即:裝飾,偏重對原對象功能的擴展,擴展后的對象仍是是對象本身。
-
代碼
+關注
關注
30文章
4823瀏覽量
68985 -
應用程序
+關注
關注
38文章
3292瀏覽量
57885 -
線程
+關注
關注
0文章
505瀏覽量
19745
原文標題:來自BAT大牛總結的常用設計模式匯總~
文章出處:【微信號:mcuworld,微信公眾號:嵌入式資訊精選】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論