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