大话设计模式(更新ing...)
目錄
單例模式:
簡單工廠模式
工廠方法模式
抽象工廠模式
策略模式
觀察者模式
適配器模式
模板方法模式(模板模式)
裝飾者模式
靜態代理模式
動態代理模式
責任鏈模式
享元模式
迭代器模式
單例模式:
方式一:拿去吧!
如果一個對象被new了多次,但無法保證每個對象都會被使用,這時候就會很浪費空間,而且如果訪問量巨大,服務器也要承擔很大的壓力。出現這種情況,一些Java大神們當然不會放任著不管,于是經過一番潛心研究,就出現了一種新的獲得對象的方式——單例模式。
單例模式大概做的事情就是通過自己的設計,讓當前的這個類只能產生一個對象(但是根據特定情況以及自己對于這一塊的設計,不能一刀切的都去用這個方法,比如一個person類,每個person都有自己不同的屬性,怎么能都用一個person來做事情呢)。
如果做?
構造方法:前面我們為了不能隨便創建當前對象,已經給構造方法私有化,所以排除此方法。
代碼塊:代碼塊會在對象創建的時候就加載了,但是沒有返回值,同樣獲取不到對象,排除。
普通方法:這個似乎和構造方法一樣了,每次調用這個方法同樣會創建一個對象出來,也無法做到對象時單例的。
屬性:?似乎只能使用屬性來new對象,然后通過方法來將對象傳遞。
? ? ? ????????? public Singleton single = new Singleton();
上面這個寫法很明顯是不對的,而且在做單例上面,似乎沒有任何存在的意義。
原因是啥呢?
首先,這樣寫上來就是一個StackOverflow Error。因為這是類里面的屬性,類加載以后會加載自己里面的屬性,方法等。但是當加載這個屬性的時候,發現又new了一個自己,然后又去加載自己類,又遇到一個屬性里面new了一個自己。。。陷入死循環導致棧溢出。
解決方法:保證是一份就OK啦。 public static Singleton single = new Singleton();
有些朋友可能看出還有一個問題:就是這樣寫我豈不是用類名就可以隨便調用了,Singleton.single簡直不要太簡單。所以呢,為了讓我的獲取對象的方法更有存在感,必然是不能讓您調用的嗷。
較為標準的寫法:private static Singleton single = new Singleton();
好啦。屬性對象創建好了,我怎么給你呢--->通過方法獲得。 設計一個方法,將當前唯一的對象返回出去。? ? ? ?
public static Singleton getInstance(){//注意:這里return的是single屬性中存儲對象的地址引用return single;}這種方式又叫做餓漢式,也就是:上來就創建一個對象,如同餓漢見到食物上來就吃。但是呢,這樣在整個系統執行過程中,如果創建的對象不使用,就會一直占用著內存,造成了內存空間的浪費。
方式二:不用,我就不做!
這種方式呢,又叫做懶漢式,就是你可以要,但是我未必會創建,等到你用的時候我再創建,這樣就很大程度上避免了內存空間浪費問題。寫法和餓漢式幾乎一致,代碼如下:
private Singleton(){} //我先不創建對象 private static Singleton single; public static Singleton getInstance(){//這個時候你要用了,但是我要看看是否真的沒有創建好的對象嗎,沒有我再給你if(single == null){single = new Singleton();}return single; }有利肯定有弊:這時候要考慮到線程的問題了,比如A和B兩個線程,同時需要這個對象,假如A先開辟了一塊空間single,A正準備判斷對象是否為空,這個時候時間片輪轉,到B了,B也開辟了一塊空間,然后A又創建了對象,但此時B判斷對象已經不為空了,但是自己又開辟了一塊空間,就比較沖突。當線程數更多的話,資源的爭奪就會更明顯。所以,請看下個分解。
方式三:我給鎖住,讓你搶我的東西!
在懶漢式的基礎上加一個鎖,就是我在用這個對象的時候,我要給他鎖住,你們誰也不能動,直到我用完。代碼實現如下:
private Singelton(){} private static Singleton single; //當前線程使用的時候不允許其他線程操作 public synchronized static Singleton getInstance(){if(single == null){single = new Singleton();}return single; }有利有弊:這種整個方法的執行過程中,不能有其他線程操作的方式,線程安全是解決了,萬一你用很久,誰等的起啊。
方式四:雙重判定!
就是將當前的類模板鎖住,而不是鎖住整個對象,這樣其他線程進來如果對象不是空的,就可以直接返回了,而不需要去等待加鎖的線程執行完畢,這樣大大提高了執行效率。實現如下:
private Singelton(){} private static Singleton single; public static Singleton getInstance(){//第一重判定,如果為空,加鎖,創建對象if(single == null){synchronized(Singleton.class){//第二重判定,如果為空 創建對象if(single == null){single = new Singleton();}}}return single; }有利有弊:這樣在解決了加鎖性能的問題的同時,在JVM虛擬機中,在對象的開辟和賦值的過程中,可能會產生指令重排序。本來時1 ——> 2 ——> 3的順序,可能會變成 1 ——> 3 ——> 2。
方式五:大哥來了!
屬性上添加volatile,保證屬性在加載和賦值的過程中,不會被JVM指令重排序。代碼如下:
private Singleton single(){} private static volatile Singleton single; public static Singleton getInstance(){if(single == null){synchronized(Singleton.class){if(single == null){single = new Singleton();}}} }簡單工廠模式
大家所熟知的有工廠方法模式、抽象工廠模式,那么簡單工廠模式又是什么呢?簡單工廠模式不能算作一個標準的設計模式,但是呢,在開發之中又挺常用的,那么就當做一個熱身吧!
舉一個小案例:話說有一天晚上,博主突發餓疾,非要去附近的小作坊買一些關東煮吃吃。且附近幾家都有賣。那么問題來了,我新來的這個地方,還不熟悉,我還沒吃過這附近的關東煮呢,萬一關東煮里面有其他東西,出了問題,我找誰去呢??找我買東西的那家店?萬一人家無證經營跑路了呢。所以為了安全起見,我需要看到人家的營業執照才放心的買。那么這就要求這些小作坊需要被統一管理起來(頒發營業執照,你這家店可以賣關東煮),或者說的貼切代碼一些(遵循一個規則)。
好,假設有三家小作坊店,為了方便理解,類名就用拼音了(其實是我不會英語)。那么:GuanDongZhu1.java? ? GuanDongZhu2.java? ? GuanDongZhu3.java。這是我附近的三家店。現在我要給他們指定一個規則,允許他們賣關東煮。AcceptGuanDongZhu.java。既然是指定規則的類,我們就可以將它指定為借口或者父類。此例中將其指定為借口。那么三個小作坊店都實現這個借口里面的方法(賣關東煮)。代碼如下:
GuanDongZhu1.java
public class GuanDongZhu1 implements AcceptGuanDongZhu{@Overridepublic void sale() {System.out.println("GuanDongZhu1號店也售賣關東煮,博主快來吃啊");} }GuanDongZhu2.java
public class GuanDongZhu2 implements AcceptGuanDongZhu{@Overridepublic void sale() {System.out.println("GuanDongZhu2號店售賣關東煮,博主快來吃啊");} }GuanDongZhu3.java
public class GuanDongZhu3 implements AcceptGuanDongZhu{@Overridepublic void sale() {System.out.println("GuanDongZhu3號店關東煮最好吃,博主快來吃啊");} }AcceptGuanDongZhu.java
public interface AcceptGuanDongZhu {//售賣關東煮的接口方法(營業執照) 默認用 public abstract修飾void sale(); }TestMain.java
public static void main(String[] args) {//多態來創建子類對象AcceptGuanDongZhu sale = new GuanDongZhu1();sale.sale(); }好,營業執照給了,小作坊也遵循了,但是!新的問題來了。用戶這樣用,是不是知道的太多了。首先,他知道了我的接口規則(實現了AcceptGuanDongZhu這個接口),其次,我具體的實現類也讓他知道了,這完全違背封裝隔離的思想啊。那么我就需要把用戶和店隔開,用戶可以看見我的實現規則,但是呢,我不能讓你知道我的具體實現類是哪個,你可以通過傳參數的方式,告知我想去哪個店,我給你找。而中間這個隔離層呢,就是我們今天的重點了——Factory。就是你告訴我工廠,你要買關東煮了,如果你傳給我一個參數(店名),那我就把這個店給到你,不傳,我就隨便給你一個。按照這種封裝隔離的思想,把具體的實現類包裝起來。代碼實現如下:
GuanDongZhuFactory.java
//調用我工廠 我要給你返回一個店 你給我傳一個店名 或者不傳我就隨機給你一個public AcceptGuanDongZhu getGuanDongZhu(String name){//下面就簡單做個判斷,重要的是思想if(!"".equals(name) && "GuanDongZhu1".equals(name)){return new GuanDongZhu1();}else if(!"".equals(name) && "GuanDongZhu2".equals(name)){return new GuanDongZhu2();}else if(!"".equals(name) && "GuanDongZhu3".equals(name)){return new GuanDongZhu3();}else{//啥也不傳,我給你選一個return new GuanDongZhu1();}}那么這時候用戶找店的方式就應該變成:
TestMain.java
public static void main(String[] args) {//先創建工廠GuanDongZhuFactory factory = new GuanDongZhuFactory();AcceptGuanDongZhu sale = factory.getGuanDongZhu("GuanDongZhu1");sale.sale();}工廠方法模式
引子:為什么會有工廠方法模式?
在上面的簡單工廠模式中,用戶通過創建工廠對象來獲取目標對象(GuanDongZhu對象),那么由于關東煮不止一家,用戶肯定有自己想去的一家,在簡單工廠模式中依靠傳參數來決定選擇哪一家,那這就意味著用戶需要了解參數的意義。而且啊,添加了參數,我工廠處理的時候豈不是多了很多的判斷,如:下面就是簡單工廠中的工廠要做的事情。
if(!"".equals(name) && "GuanDongZhu1".equals(name)){return new GuanDongZhu1();}else if(!"".equals(name) && "GuanDongZhu2".equals(name)){return new GuanDongZhu2();}else if(!"".equals(name) && "GuanDongZhu3".equals(name)){return new GuanDongZhu3();}else{//啥也不傳,我給你選一個return new GuanDongZhu1();}這樣,每新開一家店,大工廠就要多一個判斷,是不是挺麻煩啊??墒遣慌袛嗄?#xff0c;我又不知道該創建哪個具體的對象,唉,好煩,不干了!對,不干了——無為而治。那怎么辦?這樣吧,你大工廠把自己的方法變成抽象的,然后我再找人去專門創建對象,但是呢,你大工廠要把我創建好的對象給人家用戶好吧。“好!”。OK,大工廠的獲取對象的方法變成抽象了,那類也就是抽象的了。創建對象的事,我再找人,小弟多。我給每一個關東煮店都配一個創建對象的小弟(小工廠),然后由大工廠去把這個小工廠找過來遞給用戶。部分代碼實現如下:
GuanDongZhuFactory.java? ?總工廠類
public abstract class GuanDongZhuFactory {//判斷太麻煩 不干了! 你再找小弟去創建對象吧,我只負責給你找小弟,然后送給用戶public abstract AcceptGuanDongZhu chooseGuanDongZhu();//為了不讓用戶知道 我大工廠又去找小工廠干活了 掩蓋住我懶惰的事實public AcceptGuanDongZhu getGuanDongZhu(){return chooseGuanDongZhu();} }GuanDongZhu1Factory.java? ?每個店的小工廠(負責創建對象)
public class GuanDongZhu1Factory extends GuanDongZhuFactory{//一號店的小弟 創建了一號店對象@Overridepublic AcceptGuanDongZhu chooseGuanDongZhu() {return new GuanDongZhu1();} }TestMain.java
public static void main(String[] args) {//大工廠拿著小工廠給創建好的對象 借花獻佛 送給了用戶 賣關東煮成功GuanDongZhuFactory factory = new GuanDongZhu1Factory();AcceptGuanDongZhu sale = factory.getGuanDongZhu();sale.sale();}給每一家店都分配一個小工廠,這個小工廠只創建這一家的對象,就省去了判斷的步驟,用戶直接創建對應的那一家店,這樣只暴露給用戶工廠類,具體實現類用戶是不知道的。
所謂工廠方法,中的方法指的就是總工廠里面的抽象方法,小工廠通過繼承大工廠,然后重寫這個方法實現對象創建。 工廠方法模式中的 “ 方法 ”。
public abstract AcceptGuanDongZhu chooseGuanDongZhu();抽象工廠模式
模擬計算機組件(主板,CPU)來描述此模式,(相關知識可能有誤,見諒)先實現沒有抽象工廠模式,只是簡單工廠模式,將兩者進行對比。
簡單工廠模式實現如下:
AMDCPU.java
public class AMDCPU implements CPU {//屬性 名字private String name;//CPU的管腳數private int basePins;public AMDCPU(String name, int basePins) {this.name = name;this.basePins = basePins;}//CPU用戶核心計算 實現一個接口 CPU 統一進行計算@Overridepublic void centerCalculate() {System.out.println("這是AMD的CPU" + this.name);} } InterCPU.java public class InterCPU implements CPU{private String name;private int basePins;public InterCPU(String name,int basePins){this.name = name;this.basePins = basePins;}//CPU用戶核心計算 實現一個接口 進行統一管理@Overridepublic void centerCalculate() {System.out.println("這是Inter的CPU" + this.name);} }CPU.java? 統一管理的接口
void centerCalculate();CPUFactory.java? 創建對象的簡單工廠
//默認 1 是 Inter 2 是 AMDpublic static CPU createCPU(int type){if(type == 1){return new InterCPU("Inter",1155);}else{return new AMDCPU("AMD",775);}}ASUSMainBoard.java??
private String name;//針對于CPU上面的管腳數private int CPUPins;public ASUSMainBoard(String name,int CPUPins){this.name = name;this.CPUPins = CPUPins;}//實現統一接口 安裝主板@Overridepublic void installMainBoard() {System.out.println("ASUS主板"+this.name+",CPU針腳數為:"+this.CPUPins);}MSIMainBoard.java
private String name;private int CPUPins;public MSIMainBoard(String name,int CPUPins){this.name = name;this.CPUPins = CPUPins;}//實現統一接口 安裝主板@Overridepublic void installMainBoard() {System.out.println("MSI主板:"+this.name+",CPU針腳數為:"+this.CPUPins);}MainBoard.java? 統一接口
void installMainBoard();MainBoardFactory.java? 創建對象的簡單工廠類
//傳遞參數:主板類型,默認 1 是 MSI 2 是 ASUSpublic static MainBoard createMainBoard(int type){if(type == 1){return new MSIMainBoard("MSI",1155);}else{return new ASUSMainBoard("ASUS",775);}}TestMain.java
public static void main(String[] args){//用戶根據需求向工程師要求用什么樣的主板 什么樣的CPUComputerEngineer engineer = new ComputerEngineer();engineer.assembleComputer(1,2);}問題:這樣讓用戶來傳遞主板和CPU的參數,可能會出現創建的對象和其他工廠的不匹配,比如創建了一個針腳為1155的主板,卻創建了針腳為775的CPU,這樣是不匹配的。而此時,抽象工廠模式,是更注重一系列對象互相之間的依賴關系。類似于資源的整合,同樣針腳數的主板和CPU可以當做一套來創建返回給用戶,這種方法的實現方式就是:使用一個抽象工廠來規定一套規則(主板和CPU),然后指定相應個計劃,讓用戶來傳遞這個計劃給工程師(相當于套餐),工程師組裝電腦給用戶。新增代碼如下:
AbastactFactory.java
public interface AbstractFactory {MainBoard mainBoard();CPU CPU(); }PlanA.java? ?裝機方案A
/*** 一套計劃————>實現抽象工廠類 為用戶提供一套可用計劃*/ public class PlanA implements AbstractFactory{@Overridepublic MainBoard mainBoard() {return new ASUSMainBoard("ASUS",1155);}@Overridepublic CPU CPU() {return new AMDCPU("AMD",1155);} }PlanB.java? 和A類似 不予贅述
ComputerEngineer.java??
//工程師根據用戶提供的計劃來組裝電腦public void assembleComputer(AbstractFactory plan){//創建主板和CPUMainBoard board = plan.mainBoard();CPU cpu = plan.CPU();board.installMainBoard();cpu.centerCalculate();}TestMain.java
public static void main(String[] args){//用戶只需要提供一個方案 工程師按照方案來組裝AbstractFactory plan = new PlanA();//找工程師ComputerEngineer engineer = new ComputerEngineer();engineer.assembleComputer(plan);}策略模式
舉個栗子:假設,假設哈,博主我是一個房地產開發商(美死我了)。那么我要賣房子了??蛻魜碣I房子,我們實行的是有認識的人可以打折的政策。假定用戶認識:售樓部門主管,或者是幫我蓋房子的工人,或者認識我。假定用戶認識他們就可以享受打折。否則就是普通用戶,無打折活動。那么下面的類來實現這一關系,售房。用戶給一個指定價格來買對應的房子。
Price.java? ?報價的類
//報價的類 public class Price {//該方法用來計算用戶買這個房的最終價格//參數:用戶報價 和 是否具有享受活動的關系(使用int類型來代表關系)//0 代表 蓋房子的工人 1 代表 售樓主管 2 代表 老板(我)//返回值:返回最終價格 由于買房涉及金額較大 直接使用int類型public int calculatePrice(int price,int identify){//判斷是哪種身份來進行最終價格的計算switch(identify){case 0 :System.out.println("工人的親屬來買房啦,蓋房子辛苦了,打個大折吧");return price - 100000;case 1 :System.out.println("認識我們售樓主管啊,多介紹親戚來買房啊,給便宜些");return price - 50000;case 2 :System.out.println("認識老板啊,老板叫什么?看在老板面子上給你多便宜點吧");return price - 5;default:System.out.println("不是買房的啊,不能搗亂嗷");return price + 200000000;}} }直接new Price()對象調用方法即可。那么現在看這個實現方式,缺點是不是賊多。
首先:用戶就迷惑了,你給的這個identify是啥意思啊,我該傳個啥啊??不懂。。??勺x性不好
其次:看看這方法里面,又是判斷,又是計算的,累不累啊。。而且計算過程都暴露出來了,一點都不懂得保護隱私。。
好吧好吧。改寫-->
傳遞參數identify時,可以換成一個靜態常量,或者枚舉也行。這樣可讀性會更好一些了。方法里面做的事情多呢,我就給他抽離出來唄,單獨做一個方法你去調用。請看下面修改版:
Type.java? 代表身份的類
public class Type {//靜態常量 表示身份public static final int WORKER_PRICE = 0;public static final int MANAGER_PRICE = 1;public static final int BOSS_PRICE = 2; }改版后的price.java
//改版一:將身份參數換成靜態常量 增加可讀性//一個方法做事情太多,將一些事情抽出來單獨形成一個方法public int calculatePrice(int price,int identify){int endPrice = 0;//判斷是哪種身份來進行最終價格的計算switch(identify){case Type.WORKER_PRICE :endPrice = calculateForWorker(price);break;case Type.MANAGER_PRICE :endPrice = calculateForManager(price);break;case Type.BOSS_PRICE :endPrice = calculateForBoss(price);break;default:endPrice = calculateForDefault(price);break;}return endPrice;}//抽離出來用于計算價格的方法 參數:用戶報價 返回值:返回計算出來的最終價格private int calculateForWorker(int price){System.out.println("工人的親屬來買房啦,蓋房子辛苦了,打個大折吧");return price - 100000;}private int calculateForManager(int price){System.out.println("認識我們售樓主管啊,多介紹親戚來買房啊,給便宜些");return price - 50000;}private int calculateForBoss(int price){System.out.println("認識老板啊,老板叫什么?看在老板面子上給你多便宜點吧");return price - 5;}private int calculateForDefault(int price){System.out.println("不是買房的啊,不能搗亂嗷");return price + 200000000;}好啦,身份可讀性增強了,計算方法也抽離出來了,還有什么可說的呢。當然有!!(畢竟今天的主題還沒說呢是吧~.~)。先挑問題:目前是只有三個身份,倘若有一天有一百個身份,一萬個身份,去添這個case,讓誰添誰不得瘋啊。正式一點的用語就是違反了設計模式的開閉原則。擴展性太差。這就引出了我們的策略模式。策略模式如果從代碼結構上來分析就是將原有每一個單獨計算的方法從類中拆出去,升級成為單獨的類中的單獨方法(每一個方法獨立)。實現如下:
Stragegy.java? ?策略類接口? 子類實現此接口,遵循此策略
public interface Strategy {int calculatePrice(int price); }各子類策略的實現
WorkerStrategy.java
/*** 工人的策略類 專門計算工人的買房價格*/ public class WorkerStrategy implements Strategy{@Overridepublic int calculatePrice(int price) {System.out.println("工人的親屬來買房啦,蓋房子辛苦了,打個大折吧");return price - 100000;} }ManagerStrategy.java
/*** 售樓主管的策略類*/ public class ManagerStrategy implements Strategy{@Overridepublic int calculatePrice(int price) {System.out.println("認識我們售樓主管啊,多介紹親戚來買房啊,給便宜些");return price - 50000;} }BossStrategy.java同上,不贅述。
Price.java:
//得到價格的方法//參數:價格 父類的策略public int getPrice(int price,Strategy strategy){int endPrice = 0;endPrice = strategy.calculatePrice(price);return endPrice;}策略模式未來就是來解決這種流程固定,但是具體執行有些差別的問題。
觀察者模式
舉個栗子:假設有一家甜品店。不定期推出新的甜品試吃活動。有試吃活動肯定會有試吃用戶,則用戶和甜品店之間就是 依賴關系(use - a:一個類中的一個方法使用到另一個類對象)甜品店需要有用戶來試吃? 。先來實現普通的甜品店和試吃用戶的類之間的關系。
DessertShop.java? 甜品店類
/*** 甜品店類*/ public class DessertShop {//屬性private String name;/*getter和setter方法省略*///出新品的方法 用戶來試吃//參數:不需要//返回值:新甜品public Dessert newDessert(){System.out.println("新甜品出來啦,快來免費試吃啊");Dessert dessert = new Dessert();dessert.setName("牛奶小新");return dessert;} }Dessert.java? 甜品類
/***甜品類*/ public class Dessert {private String name;/*getter和setter方法省略*/ }EatPerson1.java? 試吃員1號(不是吃人1號啊)
/*** 1號試吃員*/ public class EatPerson1 {private String name;public EatPerson1(String name){this.name = name;}//獲取新的甜品//參數:甜品店 返回值:不需要public void eatNew(DessertShop shop){Dessert dessert = new Dessert();System.out.println("試吃了"+shop.getName()+"家的"+dessert.getName()+"甜品");} }TestMain.java
public class TestMain {public static void main(String[] args){//創建甜品店對象DessertShop shop = new DessertShop("1號甜品店");//創建試吃用戶EatPerson1 person1 = new EatPerson1("1號試吃員");//用戶主動找到這個甜品店person1.eatNew(shop);} }好,那么我們看這個普通的類設計有什么問題呢,到底是什么樣的問題才能引入今天的主題來呢?
首先:現在只有一個對象,但是保不齊某一天甜品店爆火,試吃人數激增呢,還去創建這么些對象嗎??但是這個問題目前是無法解決的,但是在后期Spring的IOC就可以來做到了,所以這里暫時不去他做專門的處理。
其次:每次試吃讓客戶自己主動,這樣對象一多,做的事情就多了,是不是不太好啊,而且,我不要面子的嘛!!
好,遇到問題,解決問題。引出今天的主題-->觀察者模式。誰觀察?? 大家想一下公眾號。是不是公眾號只要有新文章就會推送給用戶啊,那么我們用戶就充當了一個觀察者的角色,來監聽著甜品店的動向,什么時候出新品,我要吃熱乎的!!!
好,那我們來改變一下類的設計:這個時候就要從依賴關系變成了 has - a的聚合關系。甜品店中存儲了很多用戶,來監聽著甜品店的動向。
基本流程:
? ? ? ? 1. 目標對象中存儲這很多觀察者
? ? ? ? ? ? ? ? ? ? ? ? 一個屬性,集合,里面存儲這很多的觀察者
? ? ? ? 2.設計一個方法
? ? ? ? ? ? ? ? ? ? ? ? 往集合中添加/刪除觀察者
? ? ? ? 3.甜品店做事情
? ? ? ? 4.甜品店通知每一位用戶來試吃甜品
代碼實現如下:
EatPerson.java? ?試吃員統一接口
/*** 試吃員統一接口*/ public interface EatPerson {void eatNew(DessertShop shop); }EatPerson1.java? ?EatPerson2.java? ?EatPerson3.java? 都實現此接口,代碼未做改變。
DessertShop.java? 改版后的甜品店類
/*** 甜品店類*/ public class DessertShop {//屬性private String name;/*getter 和 setter方法省略*///=====新版本===================================================//需要一個集合來存儲所有的觀察者//觀察者需要一個統一的規范,來當做我存儲的泛型類 接口//為什么使用ArrayList??因為這個集合遍歷操作使用的比較多private List<EatPerson> observers = new ArrayList<>();//將觀察者放入到集合中public void add(EatPerson person){observers.add(person);}//可能會移除觀察者public void remove(EatPerson person){observers.remove(person);}//通知所有的觀察者public void notity(){for(EatPerson p : observers){p.eatNew(this);}}//=====舊版本==================================================//出新品的方法 用戶來試吃//參數:不需要//返回值:新甜品public void newDessert(){System.out.println("新甜品出來啦,快來免費試吃啊");Dessert dessert = new Dessert();dessert.setName("牛奶小新");//通知每個觀察者this.notity();} }TestMain.java? 測試方法
public static void main(String[] args){//===新版本========================================//創建甜品店對象DessertShop shop = new DessertShop("1號甜品店");//創建客戶對象EatPerson1 person1 = new EatPerson1("1號試吃員");EatPerson2 person2 = new EatPerson2("2號試吃員");EatPerson3 person3 = new EatPerson3("3號試吃員");//甜品店主動 找客戶//將客戶裝到觀察者集合中shop.add(person1);shop.add(person2);shop.add(person3);//找用戶試吃shop.newDessert();}以上便是觀察者模式。
適配器模式
適配器模式可將其劃分為三類(也就是此文中要介紹的三種):
? ? ? ? 1.對象適配器
? ? ? ? 2.類適配器
? ? ? ? 3.缺省適配器
先來介紹對象適配器。為了更加貼合,就使用電腦上面的存儲卡(SD)卡來當做此模式的示例來實現吧。我們先來介紹對象適配器。
眾所周知,存儲卡是分為大卡和小卡的,電腦使用的就是正常大卡,我們的電腦上面也僅僅只留了一個接口用來插存儲卡,那我要是想使用小卡插入到電腦中該怎么辦呢?自己拿錐子開辟一個接口??那電腦鑿成什么樣子了。小玩笑(一點也不好笑)。相比同志們也都見過卡套吧,一個小卡外面套著一個大卡模型的模樣(將自己打扮成大人模樣是吧)。外面那一層是沒有芯片的,只是充當一個模具來將小卡送到卡槽中。那這樣一個卡套就是我們今天要說的“對象適配器”了。詳情見如下代碼實現:
對象適配器
假定以金士頓品牌的SD卡來當做案例。分為大卡,小卡
MiniSDCard.java? 小卡統一接口
/*** 小卡統一接口*/ public interface MiniSDCard {public void read();public void write(); }SDCard.java? 大卡統一接口
/*** 存儲卡統一讀寫數據的接口*/ public interface SDCard {public void read();public void write(); }KingstonMiniSDCard.java? ?金士頓小SD卡
/*** 金士頓牌子的小卡* 實現統一存儲卡小卡接口*/ public class KingstonMiniSDCard implements MiniSDCard {//讀取和寫入的功能public void read(){System.out.println("小卡的讀取功能");}public void write(){System.out.println("小卡的寫入功能");} }KingstonSDCard.java? 金士頓大SD卡? 和小卡實現一致? 不贅述
CoverForMiniSDCard.java? ?小卡卡套
/*** 小卡的卡套 將小卡包到卡套里面之后就是一個大卡 所以實現統一大卡接口*/ public class CoverForMiniSDCard implements SDCard {//卡套和小卡的關系??//卡套和小卡形成了一個整體 沒有卡套 小卡沒法使用 沒有小卡 卡套只是一個塑料//所以二者在類上是has - a的包含關系private MiniSDCard miniSDCard;public CoverForMiniSDCard(MiniSDCard miniSDCard){this.miniSDCard = miniSDCard;}//在讀取和寫入數據的時候 當然使用小卡來操作 因為芯片在小卡里面 卡套只是一個工具@Overridepublic void read() {this.miniSDCard.read();}@Overridepublic void write() {this.miniSDCard.write();} }Computer.java? 插卡的電腦
/*** 插存儲卡的電腦*/ public class Computer {//電腦的讀取和寫入//參數:給電腦一張卡 只需要傳遞統一大卡接口 因為小卡已經使用卡套變成了大卡樣子public void readAndWrite(SDCard sdCard){sdCard.read();sdCard.write();} }TestMain.java? 對象適配器測試類
public static void main(String[] args){//先需要一個電腦Computer computer = new Computer();//需要一張大卡 本來的大卡可以使用 所以只需要操作小卡//小卡需要卡套 所以先new小卡 有了小卡 傳遞給卡套 形成了大卡 傳遞給電腦 讀取和寫入數據成功MiniSDCard mini = new KingstonMiniSDCard();CoverForMiniSDCard cover = new CoverForMiniSDCard(mini);computer.readAndWrite(cover);}類適配器
假設有一種小卡,是可以折疊的,展開是一張大卡,一半是芯片,另一個半是塑料。我們再用這樣一種卡來實現匹配類適配器。
新增類
SpecialSDCardForMini.java? 折疊小卡? 展開是一張大卡 內含芯片
/*** 可以折疊的一種小卡* 首先,他是金士頓的一種卡 extends 體現是一個* 其次,他需要當做大卡使用 遵循統一大卡的規則*/ public class SpecialSDCardForMini extends KingstonMiniSDCard implements SDCard {@Overridepublic void read() {this.readMini();}@Overridepublic void write() {this.writeMini();} }TestMain.java? ?
public static void main(String[] args){//先要有一個電腦Computer computer = new Computer();//需要一張可折疊的卡SDCard card = new SpecialSDCardForMini();computer.readAndWrite(card);}其他類無變化。僅添加了折疊卡類和去除了對于卡套的使用。
實際底層就是對于小卡變成大卡的適配實現。
缺省適配器
缺省適配器就是相當于電腦上面的一個擴展塢,由擴展塢直接和電腦連接,其他的實現類去和擴展塢連接就可以(在擴展塢中選擇自己需要的方法,進行方法重寫)。
USBCard.java? 缺省適配器抽象類,自身實現了大卡和小卡的接口,具有通用的方法,用戶可以在其中選擇需要的方法進行重寫
/*** 抽象類 擴展塢*/ public abstract class USBCard implements MiniSDCard, SDCard {@Overridepublic void readMini(){System.out.println("小卡的讀功能");}@Overridepublic void writeMini(){System.out.println("小卡的寫功能");}@Overridepublic void read(){System.out.println("大卡的讀功能");}@Overridepublic void write(){System.out.println("大卡的寫功能");} } KingstonSDCard.java 大卡繼承拓展塢 /*** 金士頓牌子的正???電腦使用的大卡)* 繼承拓展塢類 重寫需要的方法*/ public class KingstonSDCard extends USBCard {//有讀取功能public void read(){System.out.println("大卡讀取");}//有寫入的功能public void write(){System.out.println("大卡寫入");} }KingstonMiniCard.java? 小卡繼承拓展塢
/*** 金士頓牌子的小卡* 繼承拓展塢類 實現需要的方法*/ public class KingstonMiniSDCard extends USBCard {//讀取和寫入的功能public void readMini(){System.out.println("小卡的讀取功能");}public void writeMini(){System.out.println("小卡的寫入功能");} }以上就是適配器模式的三種情況。
模板方法模式(模板模式)
話不多說,上來就干!我們今天來做一個小登錄(玩點小高級的東西)。通過實現這個登錄方法來引出我們今天的主題--模板方法模式。既然是登錄嘛,主題是設計模式,所以一切從簡,數據庫啥的就不寫了,直接來個簡易版。那也就是一個實體類(存儲用戶名密碼),一個普通用戶類,一個管理員類,一個測試類。齊活。等等,代碼附在后面。
AdminUser.java? ?存儲管理員用戶名和密碼的實體類
private String username; private String password; /*其余略。。。*/NormalUser.java? 存儲普通用戶的用戶名和密碼類
private String username; private String password; /*其余略。。。*/AdminUserLogin.java 管理員的登錄方法
//管理員登錄方法public String adminLogin(String username,String password){//假裝從dao中查詢admin的信息AdminUser admin = this.selectOne(username);//獲取加密的密碼String encryptPass = this.encryptPassword(password);if(admin != null && encryptPass.equals(admin.getPassword())){return "登錄成功";}return "登錄失敗,用戶名或密碼錯誤";}//dao查詢admin的信息public AdminUser selectOne(String username){AdminUser admin = new AdminUser("Java","666");//給管理員的密碼加個密 后存入到"數據庫"中this.encryptPassword(admin.getPassword());return admin;}//加密方法 給管理員的密碼加密public String encryptPassword(String password){System.out.println("給管理員的密碼加密了");return password;}NormalUserLogin.java? 普通用戶的登錄方法
//普通用戶登錄方法public String normalLogin(String username,String password){//調用dao方法(假裝)通過username查詢信息 判斷是否登錄成功NormalUser user = this.selectOne(username);if(!user.equals(null) && password.equals(user.getPassword())){return "登錄成功";}return "登錄失敗,用戶名或密碼錯誤";}//假裝從數據庫中查詢信息的daopublic NormalUser selectOne(String username){//先來一個NormalUserNormalUser user = new NormalUser("炸雞","123");return user;}好啦。這樣我們定睛一看!問題這不就來了嘛。這樣每一個用戶,這樣來看僅僅是一個類就搞定了信息的存儲,但是如果放到數據庫中,這可就是一個表啊,一個管理員一個用戶這就兩張表了。而且這兩個類的方法都類似,登錄方法,實體類都是一致的。是否可以將這兩個合并到一起呢。當然。這也就是今天的模板方法模式的主場了。請看以下操作。
User.java? 普通用戶和管理員共用的實體類
private String username;private String password;//好 我們假設用戶又多了一個屬性 age 管理員在賦值的時候可直接賦值為空private Integer age;LoginTemplate.java? ?登錄模板抽象類 子類繼承去實現登錄等一些登錄相關操作
/*** 將用戶和管理員相同的方法都放到這個抽象類里面 來當做一個模板* 因為抽象類是允許有非抽象方法 和 抽象方法的* 所以 保不齊有方法兩者都需要 且 操作不同 那么這個方法就當做必須重寫的方法定義到這個類里面*/ public abstract class LoginTemplate {//方法一:登錄驗證流程 這個就當做是一個統一的 不需要重寫//同時 我們也默認這個方法是不允許子類去重寫的 因為這個流程我已經規定好了 你不用改//所以我要用final來修飾這個方法 以防子類去重寫來打亂我執行的流程public final String login(String username,String password){//調用管理員和普通用戶各自的查詢信息的方法 去獲取對象信息User user = this.selectOne(username);//但是管理員的密碼是有一個加密需求的 所以需要規定一個密碼加密方法 但是不是必須重寫的 因為普通用戶不需要String encryptPass = this.encryptPassword(password);//判斷密碼是否正確if(user != null && password.equals(encryptPass)){return "登錄成功";}return "登錄失敗,用戶名或密碼錯誤";}//查詢對象信息驗證登錄是否正確//雖然這個方法兩個類都需要 但是流程又不一樣 那么好,我做一個定義 你去自己重寫好了//這個方法我只允許子類實現 但是用public修飾時,在同包中都是可見的 所以需要將其保護起來protected abstract User selectOne(String username);//密碼加密方法 不是必須重寫 誰需要誰重寫protected String encryptPassword(String password){//這個方法在這里面什么也不做 等著子類去重寫return password;} }AdminLogin.java? 管理員登錄
/*** 管理員登錄 繼承抽象模板方法 重寫查詢方法 和 密碼加密方法*/ public class AdminLogin extends LoginTemplate{@Overridepublic User selectOne(String username) {User user = new User();user.setUsername("炸雞");user.setPassword("123");return user;}@Overrideprotected String encryptPassword(String password) {System.out.println("密碼加密咯");return password;}}NormalLogin.java? 普通用戶登錄
? /*** 普通用戶登錄 繼承模板方法 實現查詢方法 不需要實現加密方法*/ public class NormalLogin extends LoginTemplate{@Overridepublic User selectOne(String username) {User user = new User();user.setUsername("Java");user.setPassword("666");return user;} }好啦。這就是模板模式啦,您覺得怎樣呢
裝飾者模式
首先。舉個栗子,假設現在有一個賣房子的團隊,那我們實現一下關于他們工資發放的流程吧(簡單的流程,因為我也不懂具體是怎么發放)。該團隊肯定有普通職員,工作年限為半年,假設其銷售額度為4000,每個月績效提成為3%;另有一位普通職員,工作年限為3年,假設其銷售額度為6000,每個月績效提成為3%,銷售額度每大于5000再在其基礎上漲幅1%;還有團隊負責人(主管),假設其銷售為7000,每個月績效提成為3%,但是還會提取超過銷售額度超過5000的1%,以及團隊總額的1%。下面是普通的實現,不含設計模式。
User.java? 描述職員信息
//姓名private String name;//工資private double salary;//身份 普通/主管private String identity;UserBox.java? ?替代數據庫,存儲職員信息
public class UserBox {//使用一個map集合private static HashMap<String,User> userBox = new HashMap<>();static{userBox.put("Ming",new User("小明",4000,"普通"));userBox.put("Gang",new User("小剛",6000,"普通"));userBox.put("Hong",new User("小紅",7000,"主管"));}//設計一個方法 獲取集合public static HashMap<String,User> getUserBox(){return userBox;}//設計一個方法 通過名字獲取對應的人信息public static User getUser(String user){return userBox.get(user);} }CalcBonus.java? 計算績效獎金
public class CalcBonus {//通過給定的名字 來計算其對應的獎金public double calcBonus(String name){double sum = 0;//分別找三個小弟 對三種獎金制度進行分別的計算//操作一:計算基礎獎金sum += baseBonus(name);//操作二:計算銷售額大于5000的獎金sum += beyondBonus(name);//操作三:計算主管要提取的sum += extraBonus(name);return sum;}//小弟一:計算基礎獎金private double baseBonus(String name){User user = UserBox.getUser(name);double sum = 0;sum = user.getSalary() * 0.03;return sum;}//小弟二:計算銷售額大于5000的獎金private double beyondBonus(String name){User user = UserBox.getUser(name);double sum = 0;double salary = user.getSalary();if(salary >= 5000){sum = salary * 0.01;}return sum;}//小弟三:計算主管提取的獎金private double extraBonus(String name){double sum = 0;//查看當前name是否為主管if("主管".equals(name)){// 遍歷所有職員double all = 0;for(String key : UserBox.getUserBox().keySet()){//將所有職員的工資加起來 然后乘以0.01 就是主管要提取的團隊獎金all += UserBox.getUser(key).getSalary();}//提取sum = all * 0.01;}return sum;} }這是普通的不帶有設計模式的一個實現方式,缺點是很明顯的--->可擴展性極差,也就是說如果我可能在某一天改變獎金策略,或者不發獎金,這樣修改起來就很麻煩,而且如果我要新增一個獎金政策,還需要去CaluBonus類中新增一個方法去計算對應的計算策略,那萬一我已經將項目打包呢,是否又違反了開閉原則。there is problem,there is slove(塑料英語,別介意)。裝飾者模式來咯。我們可以把裝飾者模式想象成買煎餅的場面。首先肯定是一張餅躺在那,上面才能"裝飾"火腿腸啊,雞柳啊,生菜啊等等的。那我們來簡單的實現一下。(每個步驟會在代碼中有較為詳細的注釋)。
從表層到底層的次序來展示代碼:
Bonus.java? 基礎的獎金類
/*** 最基礎的計算獎金的類 相當于煎餅中的一個餅*/ /*** 將每個裝飾者都實現了Decorator抽象類 那么現在 我當前這個類和Decorator又是什么關系呢,* 現在是我寫的方法名一致 可能知道他們是計算獎金的 但是如果方法不一致呢,而且用戶也不知道啊* 那不如再來一個超級父類 將他倆綁定到一起 產生一個裝飾關系 用接口定義一個規則 用戶用起來也方便 SuperBonus.java*/ public class Bonus implements SuperBonus{//設計一個方法 用來計算獎金public double calcuBonus(String name){//老板心情不好 獎金全部取消!! 好了,這個方法沒有獎金而言System.out.println("默認沒有獎金");double num = 0;return num;} }MonthBonus.java? 裝飾者1 月度基本獎金
/*** 每個月的基本的0.03獎金* 在Bonus之后 獲取該類的獎金* 相當于煎餅中的一個火腿腸-->是額外加的*/ public class MonthBonus extends Decorator{//直接繼承裝飾者類private Decorator d;public MonthBonus(Decorator d){this.d = d;} // private Bonus bonus;public double calcuBonus(String name){//先獲得bonusdouble sum = d.calcuBonus(name);//計算自己的sum += UserBox.getUser(name).getSalary() * 0.03;return sum;} }BeyondBonus.java? 裝飾者2? 銷售額超額獎金
/*** 銷售額度超過5000塊的1%的提成(裝飾者1)* 會在Bonus之后才有 先有基礎的 再有外加的* 相當于煎餅中的雞柳-->額外的*/ public class BeyondBonus extends Decorator{//直接繼承裝飾者對象 // private Bonus bonus; // private MonthBonus bonus;private Decorator d;public BeyondBonus(Decorator d){this.d = d;}public double calcuBonus(String name){//先獲得基礎的獎金(管你有沒有,先要再說)double sum = d.calcuBonus(name);//計算我自己辛苦得來的那1%獎金sum += UserBox.getUser(name).getSalary() * 0.01;return sum;} }ExtraBonus.java? 裝飾者3??主管提取獎金
/*** 主管提取全部職員的總額的1%的獎金類* 前提是:我先要基礎的 再要銷售額大于5000的那1% 再計算我自己的* 相當于煎餅中的 雞排-->額外的*/ public class ExtraBonus extends Decorator{private Decorator d;//構造方法 創建對象public ExtraBonus(Decorator d){this.d = d;}//既然都是裝飾者 何不如歸為一個類下-->繼承抽象類 Decorator // private Bonus bonus; // private BeyondBonus beyond;public double calcuBonus(String name){//基礎獎金double sum = d.calcuBonus(name);//5000塊的1%提成sum += d.calcuBonus(name);//計算我自己的總額1%//遍歷mapdouble all = 0;for(String key : UserBox.getUserBox().keySet()){all += UserBox.getUser(key).getSalary();}sum += all * 0.01;return sum;} }Decorator.java? 抽象類? 規定裝飾者的模板
/*** 裝飾者的父類 規定一個裝飾者的模板* 實現統一計算獎金的接口 統一方法名*/ public abstract class Decorator implements SuperBonus{//計算獎金的抽象方法public abstract double calcuBonus(String name); }SuperBonus.java? ?接口 統一Bonus和Decorator方法名
/*** 聲明Bonus和Decorator的關系 讓計算獎金的方法名統一 用戶用起來較為方便*/ public interface SuperBonus {public double calcuBonus(String name); }裝飾者模式到此結束~~
靜態代理模式
同樣的,舉個栗子先。今天我們來舉關于手機的例子。手機,當前要工廠了,假設,假設一下,現在的手機售賣店都是前面是門店,后面是工廠,當然,工廠是隱藏起來的。就舉iPhone的例子吧,一個手機店配著一個工廠,那么手機店和工廠就是use-a的依賴關系啦。好,那我們開始實現。老樣子,先實現最基礎的類和類的關系,然后一點點增加,直到形成設計模式。
iPhone.java? 描述手機
/*** 這個類是用來描述手機的*/ public class iPhone {private String name;private String color;/*構造方法略*///手機的一些功能public void game(){System.out.println("蘋果手機打游戲反應快");}public void call(){System.out.println("蘋果手機打電話流暢");}public void photo(){System.out.println("蘋果手機拍照清晰");} }AppleStore.java? 手機店(包含工廠)
/*** 這個類是蘋果手機的專賣店 可以理解為他的工廠*/ private class AppleStore {//這個手機店因為帶著一個工廠 所以他可以生產手機和賣手機public iPhone productPhone(){iPhone phone = new iPhone("iPhone11","white");System.out.println("生產一臺手機");return phone;}public iPhone salePhone(){iPhone phone = this.productPhone();System.out.println("售賣手機");return phone;} }測試方法直接創建手機店,賣手機即可,這里不在描述。
那現在有這么一個情況,人們都知道這個手機好賣,那么就有商人也想賣這種牌子的手機,于是申請為代理商(這個代理賣真機的過程不再描述,重點在后面),代理商賣了一段時間之后,用戶信譽已經養成差不多了,開始覺得里面似乎有利可圖,可以將采購回來的真機里面的零部件換上那么一兩個,反正一般的客戶也看不出來。這樣的事情愈演愈烈,終于有一天,整部手機都變成了假的。當全部都變成假的了的時候,還有必要從人家那里拿貨自己再去處理嗎,那也太麻煩了,有那時間早就自己造一個工廠了。對!自己來一個山寨工廠,造山寨手機。然后拿來當做真機賣。但是用戶不知道啊,還以為代理商賣的是真手機,還來這買,殊不知已經被偷偷地換過了。
經過一番收拾,把代理商門店裝修的本店一模一樣,連客服都穿一樣的衣服,然后再將手機變成和真機一樣的外觀,一樣的功能方法。用戶在購買的時候就無法分清楚所拿到的是真手機還是假手機。
ProxyStore.java? 代理商
/*** 代理商 實現Store接口 裝修風格和真手機店一模一樣 并且要一樣的賣手機的方式和客服*/ public class ProxyStore implements Store{//代理商全部賣假手機 自己造一個山寨工廠 生成rPhoneprivate rPhone productPhone(){rPhone phone = new rPhone("rPhone11","white");return phone;}//售賣rPhone@Overridepublic rPhone salePhone(){//也可以售賣真手機 這個可以根據自己設計來選擇(遇到漂亮的小姐姐就賣假的,制造售后機會)rPhone phone = this.productPhone();System.out.println("賣假手機咯");return phone;} }rPhone.java? 假手機
/*** 用來描述假手機 仿真iPhone 和真iphone有一樣的功能*/ public class rPhone implements Phone{private String name;private String color;public rPhone(String name,String color){this.name = name;this.color = color;}public void call(){System.out.println("假iPhone,打電話,你說什么,我聽不清");}public void game(){System.out.println("假iPhone,打游戲,閃退");}public void photo(){System.out.println("假iPhone,拍照,自動打碼");} }Phone.java? ?統一手機接口 長得一樣 功能方法也一樣
/*** 統一手機接口 長得一樣 功能一樣*/ public interface Phone {public void game();public void photo();public void call(); }Store.java
/*** 統一手機店的裝修風格 看不出來真假 實現一個賣手機的方法 連客服都一樣*/ public interface Store {public Phone salePhone(); }靜態代理模式一般在進行架構的時候可能會使用上,因為這樣在需要更換一些已實現的功能或底層架構的時候不會對使用者造成影響。(偷偷地)
動態代理模式
這次要介紹的動態代理模式就以靜態代理模式例子為基礎來講吧,這樣比較好理解一些。代碼實現依然保持靜態代理的形式,保留了:Store.class,iPhone.class,AppleStore.class三個類,只是排除了靜態代理里面的代理商。這次的代理商我不想自己去創建了,我想讓別人創建好給我,我只需要拿著這個創建好的代理對象去賣手機即可。那么我這個“別人”應該是誰呢,很明顯,就是一個工廠嘛,就是我這個手機的代工廠,我把手機的規格,各種配置啥的給你,你按照這個要求去生產,然后給我,我拿去賣,這樣多好啊。
StoreFactory.java? ?代理工廠類? 幫我做手機
/*** 這個類就相當于是一個代工廠 用來創建實例對象 做一些這個對象應該做的事情,返回出去一個用戶需要的結果*/ public class StoreFactory {//可能會有不同的人來找我做事(我做的事情肯定不是單一的生成iPhone手機,肯定還能干其他事情)//所以對于不同的用戶調用可能要返回不同的代理對象 泛型//參數呢? 用戶給我一個需要代理的對象(給我一個需要去代理的對象)public static <T>T getInstance(Class clazz){//創建代理類的對象是java提供的工具方法//參數:三個參數://1.類加載器 把需要代理的這個對象加載進來ClassLoader loader = clazz.getClassLoader();//2.代理誰 將要代理的對象包成一個數組傳遞Class[] classes = new Class[]{clazz};//3.代理之后我需要做什么事情InvocationHandler handler = new InvocationHandler() {@Override//這個方法就是代理這個對象之后我改做什么事情public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是代工廠,我要生產手機了");iPhone phone = new iPhone("iPhone11","white");return phone;}};T proxy = (T) Proxy.newProxyInstance(loader, classes,handler);//將這個代理的對象返回出去return proxy;} }由于事情都交給了代理工廠來做,AppleStore只是規定代理工廠要做什么,所以將其抽象成為一個接口。
AppleStore.java?
/*** 這個類是蘋果手機的專賣店 可以理解為他的工廠 實現統一手機店接口*/ public interface AppleStore extends Store {@Overridepublic iPhone salePhone() ; }測試類? 通過代理工廠來獲得手機對象并售賣出去
public static void main(String[] args) {//找代理工廠 來幫我生產手機 我要賣手機啦//但是在這個AppleStore里面 除了一個賣手機的方法之外已經不做任何其他事情了 事情都交給了代理工廠來做//所以需要將其抽象為一個接口 (只需要告訴代工廠你這個手機的型號之類的信息即可)AppleStore store = StoreFactory.getInstance(AppleStore.class);iPhone phone = store.salePhone();//使用手機phone.call();phone.game();phone.game(); }以上就是動態代理模式了,我自己對于動態代理的理解就是,對于代理對象的動態,對于不能的代理對象會做不同的事情,而不是只能賣手機,就像是一個代工廠,不一定只能做一個產品。誰來找我做我可以干的事情,我都干。就是這樣一個動態的實現。
責任鏈模式
責任鏈模式在比較多的領域中如tomcat,Spring等中都有使用。個人覺得理解起來還是比較簡單的。同樣的,例子先行。由于和鏈相關,那咱就用鏈表來引出這個模式的問題。LinkedList想必大家都知道。我找你,你找他嘛(山有木兮木有枝~.~)。由于大家可能對于鏈表比較熟悉了,這里就不做代碼實現了。我們只用鏈表來引出責任鏈模式要解決的問題。大家都知道,鏈表是有頭結點的。那么為了便于理解,我們暫且把這個頭結點比作是一位老師,后面的其他結點都是一班里面的同學。然后這個連接的過程我們把他比作老師喊同學回答問題的過程(怎么樣,很形象吧)。那么放在鏈表上這樣的關系就是,老師先找一個學生,然后學生再找下一個學生??雌饋砗孟裢玫?#xff0c;老師只需要找一次,剩下的同學都會自動去回答。但是呢,這樣下來,每次提問問題都這樣,那坐在后排的同學豈不是太爽了,因為有充足的時間去思考這個問題,但這似乎違背了老師想傳授的一個對于知識快速做出反應的能力。所以這問題也就很明顯了。那么我們只好讓老師一個一個的去找學生了,那么老師找了一個學生之后,什么時候開始找下一個學生呢?——>當前這個同學回答完畢之后給老師一個反饋,老師得到這個反饋之后,繼續找下一個同學。大家看這個方法是不是很好呢。那么這也就是責任鏈模式了。下面是代碼實現以及較為詳細的注釋講解。
ApplicationChain.java? ?相當于教師類
/*** 這個類好比是老師 用來找同學回答問題*/ public class ApplicationChain {//記錄當前提問到哪個同學(保證不能重復)private int counts = 0;//老師這個班級里面肯定要有學生啊//用一個集合 來存儲所有的學生private ArrayList<Node> nodes = new ArrayList<>();//提供一個方法 將學生交給老師管理public void addNode(Node node){nodes.add(node);}//老師喊同學回答問題//同學在哪?? 集合里面public void doFilter(){//從集合里隨機獲取一個 暫不考慮越界的問題(主要是設計模式)Node node = nodes.get(counts++);//找同學回答問題node.doFilter(this);} }Node.java? 相當于學生類
/*** 這個類好比是每個同學 在一個班級里面*/ public class Node {//學生的屬性private int id;public Node(int id){this.id = id;}//回答問題的方法//給誰回答呢?老師//那這個老師我從哪獲得呢?//屬性? 學生有一個老師?不合適吧//傳參? 每個同學有一個老師?多個同學可以對應同一個老師 可以public void doFilter(ApplicationChain chain){//回答問題System.out.println(this.id+"號同學回答問題");//問題回答完畢了 干什么??//告訴老師 我回答完了,你可以找下一個同學了 我自己不需要知道下一個是誰//我自己問題回答對了,自己偷著樂,回答錯了反省去,別管下一個是誰//找老師chain.doFilter();} }TestMain.java? 測試類
public static void main(String[] args) {//創建學生類Node node1 = new Node(1);Node node2 = new Node(2);Node node3 = new Node(3);//創建老師類ApplicationChain chain = new ApplicationChain();//將學生交給老師管理chain.addNode(node1);chain.addNode(node2);chain.addNode(node3);//找學生回答問題chain.doFilter();}以上就是責任鏈模式了,個人覺得理解起來還是比較簡單的。
享元模式
所謂享元模式:享-->共享? ? 元-->單元(對象) ,是一種結構型的設計模式,主要就是為了減少對象的創建(和單例模式是有區別的)。
同樣的,我們用一個例子來引出需要介紹的內容。就舉一個學生去圖書館借書的例子吧。那么在這個例子的基礎上,會產生出三個對象:圖書館、書、學生。圖書館里面有書,學生可以使用圖書館來實現借書操作。圖書館默認每個學校只有一個,所以需要將其變成一份(單例的)。
最簡單的享元模型:圖書館中的每一類書都只有一本,每次學生來借這個書,都會創建一個新的當前書對象(相當于圖書館會每次買一本新的這一類的書),但是考慮到學生會歸還書籍的,當所有的書都歸還回來極有可能導致圖書館放不下。那么就考慮到在圖書館中引入書柜,將同一名字的書多放幾本,然后供多個學生循環使用。
改版后的享元模型:圖書館中引入了書柜(一個集合,用來存儲同一名字的書籍),將同一名字的書存多份,并在初始化時為每一本書設置一個借閱狀態(boolean),這樣同學們就可以共享這幾本書,而不至于每次都去買新的。這就達到了對象的共享,而不是每次都去創建新的。學生每次取圖書館借書,圖書館為學生發放對應的書籍(或是人工,或是自動),而不是學生自己去取,也就將將書對象隱藏到圖書館里面,學生是看不到的,所以圖書館從書柜拿書的過程學生也是不知道的,學生只負責拿到書去看,看完歸還,剩下的操作都是圖書館來做的。
享元模式整體的思想也就是:盡量少的去減少同一類型對象的創建,能復用盡量復用,從而減少內存空間的浪費,同單例模式不同,單例模式是將對象變成一份的,大家共享;而享元模式是將對象變成多份大家輪詢共享。
(以上享元模式的總結為本人學習之后的個人理解)
迭代器模式
這里的迭代器模式是和java.util.Iterator對象是一樣的。就是在遍歷時有一個統一的規則,方便于遍歷操作。這里假設有一個餐館,專門用來做快餐,有自己的服務員,有自己的菜單。在某一天,這家餐館里面入駐了一家蛋糕店。這家蛋糕店也有自己的菜單,但是由于店面太小,沒有服務員,就把請服務員的錢給到快餐店,讓快餐店來 幫忙售賣自己家的產品。那么這個時候服務員就需要每來一位顧客就要拿著兩個菜單給顧客點菜。這樣對于服務員來說是很麻煩的。所以我們希望有一個統一的規則來將這兩個菜單合并到一起(將所有的菜提供給顧客,自己選擇)。這就需要要求兩家店面都實現一個接口(自定義的 Iteraotor)。這樣每個店面在初始化的時候將菜單構造出來,然后將其放入到一個菜單集合中,當兩個門店都這樣做了之后,我兩個門店的菜單就交到迭代器手上了。然后服務員通過獲取兩個門店的迭代器對象(菜單),開始自己的遍歷,將所有菜單全部展示出來。
以下是代碼實現:
Iterator.java? ?自定義的迭代器統一規則
boolean hasNext(); Object next();MenuItem.java? 描述菜品
private String name;//名稱private String description;//菜品描述private double price;//價格Restaurant.java? ?快餐店 用于初始化菜單
public DinerMenu() {menuItems = new MenuItem[MAX_ITEMS];//4個長度//這里面添加本店的菜品}//為了讓服務員點菜方便 將菜單全部拿走public MenuItem[] getMenuItems() {return menuItems;}//提供一個獲取迭代器對象的集合public Iterator createIterator(){return new DinerMenuIterator(menuItems);}RestaurantInterator.java? 實現迭代器規則的快餐店
public class RestaurantInteratorimplements Iterator{private MenuItem[] items;private int position = 0;//迭代位置public RestaurantInterator(MenuItem[] items) {this.items = items;}public boolean hasNext() {if(position >= items.length || items[position] == null) {return false;}else {return true;}}public Object next() {MenuItem menuItem = items[position++];return menuItem;} }Cake.java? 入駐的蛋糕店,實現方式和餐館一致,不贅述
Waiter.java? ?服務員類 獲取兩個門店的迭代器對象,遍歷兩個店的菜單
public void printMenu(){//獲取兩個迭代器對象Iterator cit = cake.createIterator();Iterator rit = restaurant.createIterator();//讓小弟幫我們迭代this.myprint(cit);this.myprint(rit);}//設計一個小弟方法 迭代private void myprint(Iterator iterator){while(iterator.hasNext()){MenuItem menuItem = (MenuItem) iterator.next();System.out.print(menuItem.getName()+", ");System.out.print(menuItem.getPrice()+", ");System.out.println(menuItem.getDescription());}}創作不易,辛苦點個贊吧~~
總結
以上是生活随笔為你收集整理的大话设计模式(更新ing...)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于springdatajpa 注解@C
- 下一篇: 数学,离一个程序员有多近?