設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
GitHub地址
文章說(shuō)明
一個(gè)Demo,集合常用的十種設(shè)計(jì)模式,每個(gè)模式使用易被人們接受的案例講述,按模式分包,使用設(shè)計(jì)模式前后對(duì)比,界面顯示定義講解,讓你更深刻的了解每種設(shè)計(jì)模式。
大部分案例來(lái)自張鴻洋的博客。如有錯(cuò)誤歡迎指正,如有侵權(quán),請(qǐng)聯(lián)系我刪除。
項(xiàng)目結(jié)構(gòu)
設(shè)計(jì)模式分為三種類型,共23種:
- 創(chuàng)建型模式:單例模式、抽象工廠模式、建造者模式、工廠模式、原型模式。
- 結(jié)構(gòu)型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
-
行為型模式:模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式、職責(zé)鏈模式(責(zé)任鏈模式)、訪問(wèn)者模式。
上面鏈接點(diǎn)擊直接跳轉(zhuǎn)到GitHub對(duì)應(yīng)模式解析內(nèi)
博客目錄
- 1 . 設(shè)計(jì)模式 觀察者模式(Observer Pattern) 以微信公眾服務(wù)為例
- 2 . 設(shè)計(jì)模式 工廠模式(Factory Pattern) 從賣肉夾饃說(shuō)起
- 3 . 設(shè)計(jì)模式 單例設(shè)計(jì)模式(Singleton Pattern) 完全解析
- 4 . 設(shè)計(jì)模式 策略模式(Strategy Pattern) 以角色游戲?yàn)楸尘?/a>
- 5 . 設(shè)計(jì)模式 適配器模式(Adapter Pattern) 以手機(jī)充電器為例
- 6 . 設(shè)計(jì)模式 命令模式(Command Pattern) 管理智能家電
- 7 . 設(shè)計(jì)模式 裝飾者模式(Decorator Pattern) 帶你重回傳奇世界
- 8 . 設(shè)計(jì)模式 外觀模式(Facade Pattern) 一鍵電影模式
- 9 . 設(shè)計(jì)模式 模版方法模式(TemplateMethod Pattern) 展現(xiàn)程序員的一天
- 10 . 設(shè)計(jì)模式 狀態(tài)模式(State Pattern) 以自動(dòng)售貨機(jī)為例
對(duì)應(yīng)模式所在的包
模式分析
1. 觀察者模式
定義了對(duì)象之間的一對(duì)多的依賴,這樣一來(lái),當(dāng)一個(gè)對(duì)象改變時(shí),它的所有的依賴者都會(huì)收到通知并自動(dòng)更新。
對(duì)于JDK或者Andorid中都有很多地方實(shí)現(xiàn)了觀察者模式,比如XXXView.addXXXListenter , 當(dāng)然了 XXXView.setOnXXXListener不一定是觀察者模式,因?yàn)橛^察者模式是一種一對(duì)多的關(guān)系,對(duì)于setXXXListener是1對(duì)1的關(guān)系,應(yīng)該叫回調(diào)。
專題接口:Subject.java ;
/**
* 注冊(cè)一個(gè)觀察者
*/
public void registerObserver(Observer observer);
/**
* 移除一個(gè)觀察者
*/
public void removeObserver(Observer observer);
/**
* 通知所有觀察者
*/
public void notifyObservers();
-
3D服務(wù)號(hào)的實(shí)現(xiàn)類:ObjectFor3D.java
@Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(msg); } } /** * 主題更新信息 */ public void setMsg(String msg) { this.msg = msg; notifyObservers(); }
所有觀察者需要實(shí)現(xiàn)此接口:Observer.java
public ObserverUser1(Subject subject) {
subject.registerObserver(this);
}
@Override
public void update(String msg) {
Log.e("-----ObserverUser1 ", "得到 3D 號(hào)碼:" + msg + ", 我要記下來(lái)。 ");
}
-
最后測(cè)試:ObserverActivity.java
// 創(chuàng)建服務(wù)號(hào) objectFor3D = new ObjectFor3D(); // 創(chuàng)建兩個(gè)訂閱者 observerUser1 = new ObserverUser1(objectFor3D); observerUser2 = new ObserverUser2(objectFor3D); // 兩個(gè)觀察者,發(fā)送兩條信息 objectFor3D.setMsg("201610121 的3D號(hào)為:127"); objectFor3D.setMsg("20161022 的3D號(hào)為:000");
2. 工廠模式
簡(jiǎn)單列一下這個(gè)模式的家族:
-
1、靜態(tài)工廠模式
- 這個(gè)最常見(jiàn)了,項(xiàng)目中的輔助類,TextUtil.isEmpty等,類+靜態(tài)方法。
-
2、簡(jiǎn)單工廠模式(店里買肉夾饃)
- 定義:通過(guò)專門定義一個(gè)類來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。
- 根據(jù)類型直接創(chuàng)建肉夾饃:SimpleRoujiaMoFactory.java
public RoujiaMo creatRoujiaMo(String type) { RoujiaMo roujiaMo = null; switch (type) { case "Suan": roujiaMo = new ZSuanRoujiaMo(); break; case "La": roujiaMo = new ZLaRoujiaMo(); break; case "Tian": roujiaMo = new ZTianRoujiaMo(); break; default:// 默認(rèn)為酸肉夾饃 roujiaMo = new ZSuanRoujiaMo(); break; } return roujiaMo; }
-
3、工廠方法模式(開(kāi)分店)
- 定義:定義一個(gè)創(chuàng)建對(duì)象的接口,但由子類決定要實(shí)例化的類是哪一個(gè)。工廠方法模式把類實(shí)例化的過(guò)程推遲到子類。
- 對(duì)比定義:
- 1、定義了創(chuàng)建對(duì)象的一個(gè)接口:public abstract RouJiaMo sellRoujiaMo(String type);
- 2、由子類決定實(shí)例化的類,可以看到我們的饃是子類生成的。
-
提供創(chuàng)建肉夾饃店抽象方法:RoujiaMoStore.java
public abstract RoujiaMo sellRoujiaMo(String type);
具體實(shí)現(xiàn)抽象方法:XianRoujiaMoStore.java
分店依舊使用簡(jiǎn)單工廠模式:XianSimpleRoujiaMoFactory.java
-
4、抽象工廠模式(使用官方提供的原料)
- 定義:提供一個(gè)接口,用于創(chuàng)建相關(guān)的或依賴對(duì)象的家族,而不需要明確指定具體類。
- 對(duì)比定義:
- 1、提供一個(gè)接口:public interface RouJiaMoYLFactroy
- 2、用于創(chuàng)建相關(guān)的或依賴對(duì)象的家族 public Meat createMeat();public YuanLiao createYuanliao();我們接口用于創(chuàng)建一系列的原材料。
- 創(chuàng)建用于提供原料的接口工廠:RoujiaMoYLFactory.java
- 各自分店實(shí)現(xiàn)接口,完成原料提供:XianRoujiaMoYLFoctory.java
- 準(zhǔn)備時(shí),使用官方的原料:RoujiaMo.java
/** * 準(zhǔn)備工作 */ public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) { Meet meet = roujiaMoYLFactory.creatMeet(); YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao(); Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成準(zhǔn)備工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao); }
3. 單例設(shè)計(jì)模式
單例模式主要是為了避免因?yàn)閯?chuàng)建了多個(gè)實(shí)例造成資源的浪費(fèi),且多個(gè)實(shí)例由于多次調(diào)用容易導(dǎo)致結(jié)果出現(xiàn)錯(cuò)誤,而使用單例模式能夠保證整個(gè)應(yīng)用中有且只有一個(gè)實(shí)例。
-
定義:只需要三步就可以保證對(duì)象的唯一性
- (1) 不允許其他程序用new對(duì)象
- (2) 在該類中創(chuàng)建對(duì)象
- (3) 對(duì)外提供一個(gè)可以讓其他程序獲取該對(duì)象的方法
對(duì)比定義:
(1) 私有化該類的構(gòu)造函數(shù)
(2) 通過(guò)new在本類中創(chuàng)建一個(gè)本類對(duì)象
(3) 定義一個(gè)公有的方法,將在該類中所創(chuàng)建的對(duì)象返回
餓漢式[可用]:SingletonEHan.java
-
含懶漢式[雙重校驗(yàn)鎖 推薦用]:SingletonLanHan.java
private SingletonLanHan() {}
private static SingletonLanHan singletonLanHanFour;
public static SingletonLanHan getSingletonLanHanFour() {
if (singletonLanHanFour == null) {
synchronized (SingletonLanHan.class) {
if (singletonLanHanFour == null) {
singletonLanHanFour = new SingletonLanHan();
}
}
}
return singletonLanHanFour;
}
- 內(nèi)部類[推薦用]:[SingletonIn.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/singleton/inclass/SingletonIn.java)
- 枚舉[推薦用]:[SingletonEnum.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/singleton/enums/SingletonEnum.java)

###4. 策略模式
> 策略模式:定義了算法族,分別封裝起來(lái),讓它們之間可相互替換,此模式讓算法的變化獨(dú)立于使用算法的客戶。
- 以創(chuàng)建游戲角色為例子:
- 最初的游戲角色的父類:[Role.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/strategy/old/Role.java)
- 發(fā)現(xiàn)有重復(fù)代碼后,重構(gòu)后的父類:[Role.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/strategy/better/Role.java)
- 總結(jié):
- 1、封裝變化(把可能變化的代碼封裝起來(lái))
- 2、多用組合,少用繼承(我們使用組合的方式,為客戶設(shè)置了算法)
- 3、針對(duì)接口編程,不針對(duì)實(shí)現(xiàn)(對(duì)于Role類的設(shè)計(jì)完全的針對(duì)角色,和技能的實(shí)現(xiàn)沒(méi)有關(guān)系)
- 最后測(cè)試:創(chuàng)建角色:
```java
RoleA roleA = new RoleA("---A");
roleA.setiDisplayBehavior(new DisplayYZ())
.setiAttackBehavior(new AttackXL())
.setiDefendBehavior(new DefendTMS())
.setiRunBehavior(new RunJCTQ());
roleA.display();// 樣子
roleA.attack();// 攻擊
roleA.run();// 逃跑
roleA.defend();// 防御
5. 適配器模式
定義:將一個(gè)類的接口轉(zhuǎn)換成客戶期望的另一個(gè)接口,適配器讓原本接口不兼容的類可以相互合作。這個(gè)定義還好,說(shuō)適配器的功能就是把一個(gè)接口轉(zhuǎn)成另一個(gè)接口。
以充電器為實(shí)例: 手機(jī)充電器一般都是5V左右吧,咱天朝的家用交流電壓220V,所以手機(jī)充電需要一個(gè)適配器(降壓器)
一部手機(jī): Mobile.java
手機(jī)依賴一個(gè)提供5V電壓的接口: V5Power.java
我們擁有的是220V家用交流電: V220Power.java
適配器,完成220V轉(zhuǎn)5V的作用:V5PowerAdapter.java
-
最后測(cè)試:給手機(jī)沖個(gè)電:
Mobile mobile = new Mobile(); V5Power v5Power = new V5PowerAdapter(new V200Power()); mobile.inputPower(v5Power);
6. 命令模式
定義:將“請(qǐng)求”封裝成對(duì)象,以便使用不同的請(qǐng)求、隊(duì)列或者日志來(lái)參數(shù)化其他對(duì)象。命令模式也支持可撤銷的操作。(簡(jiǎn)化: 將請(qǐng)求封裝成對(duì)象,將動(dòng)作請(qǐng)求者和動(dòng)作執(zhí)行者解耦。)
- 需求:最近智能家電很火熱,假設(shè)現(xiàn)在有電視、電腦、電燈等家電,現(xiàn)在需要你做個(gè)遙控器控制所有家電的開(kāi)關(guān),要求做到每個(gè)按鈕對(duì)應(yīng)的功能供用戶個(gè)性化,對(duì)于新買入家電要有非常強(qiáng)的擴(kuò)展性。
- 1、家電的API:Door.java
- 2、把命令封裝成類:
- 統(tǒng)一的命令接口:Command.java
- 家電實(shí)現(xiàn)該接口:DoorOpenCommand.java
- 3、遙控器:ControlPanel.java
- 4、定義一個(gè)命令,可以干一系列的事情:QuickCommand.java
QuickCommand quickCloseCommand = new QuickCommand(new Command[]{new LightOffCommand(light), new ComputerOffCommand(computer), new DoorCloseCommand(door)});
controlPanel.setCommands(6, quickOpenCommand);
controlPanel.keyPressed(6);
- 5、遙控器面板執(zhí)行:CommandActivity.java
controlPanel.setCommands(0, new DoorOpenCommand(door));// 開(kāi)門
controlPanel.keyPressed(0);
7. 裝飾者模式
裝飾者模式:若要擴(kuò)展功能,裝飾者提供了比集成更有彈性的替代方案,動(dòng)態(tài)地將責(zé)任附加到對(duì)象上。
先簡(jiǎn)單描述下裝飾者模式發(fā)揮作用的地方,當(dāng)我們?cè)O(shè)計(jì)好了一個(gè)類,我們需要給這個(gè)類添加一些輔助的功能,并且不希望改變這個(gè)類的代碼,這時(shí)候就是裝飾者模式大展雄威的時(shí)候了。這里還體現(xiàn)了一個(gè)原則:類應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。
需求:設(shè)計(jì)游戲的裝備系統(tǒng),基本要求,要可以計(jì)算出每種裝備在鑲嵌了各種寶石后的攻擊力和描述:
1、裝備的超類:IEquip.java
-
2、各個(gè)裝備的實(shí)現(xiàn)類:
- eg:武器的實(shí)現(xiàn)類: ArmEquip.java
3、裝飾品的超類(裝飾品也屬于裝備):IEquipDecorator.java
-
4、裝飾品的實(shí)現(xiàn)類:
- eg:藍(lán)寶石的實(shí)現(xiàn)類(可累加): BlueGemDecorator.java
-
5、最后測(cè)試:計(jì)算攻擊力和查看描述:
Log.e("---", "一個(gè)鑲嵌2顆紅寶石,1顆藍(lán)寶石的靴子: "); IEquip iEquip = new RedGemDecotator(new RedGemDecotator(new BlueGemDecotator(new ShoeEquip()))); Log.e("---", "攻擊力:" + iEquip.caculateAttack()); Log.e("---", "描述語(yǔ):" + iEquip.description());
8. 外觀模式
定義:提供一個(gè)統(tǒng)一的接口,用來(lái)訪問(wèn)子系統(tǒng)中的一群接口,外觀定義了一個(gè)高層的接口,讓子系統(tǒng)更容易使用。其實(shí)就是為了方便客戶的使用,把一群操作,封裝成一個(gè)方法。
需求:我比較喜歡看電影,于是買了投影儀、電腦、音響、設(shè)計(jì)了房間的燈光、買了爆米花機(jī),然后我想看電影的時(shí)候,我需要一鍵觀影和一鍵關(guān)閉。
每個(gè)設(shè)備類的開(kāi)關(guān)等操作:
eg: 爆米花機(jī):PopcornPopper.java
-
/** * 一鍵觀影 */ public void watchMovie() { computer.on(); light.down(); popcornPopper.on(); popcornPopper.makePopcorn(); projector.on(); projector.open(); player.on(); player.make3DListener(); }
-
最后測(cè)試:一鍵觀影:
new HomeTheaterFacade(computer, light, player, popcornPopper, projector).watchMovie();
9. 模板方法模式
定義:定義了一個(gè)算法的骨架,而將一些步驟延遲到子類中,模版方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法的步驟。
需求:簡(jiǎn)單描述一下:本公司有程序猿、測(cè)試、HR、項(xiàng)目經(jīng)理等人,下面使用模版方法模式,記錄下所有人員的上班情況
模板方法模式中的三類角色
1、具體方法(Concrete Method)
2、抽象方法(Abstract Method)
3、鉤子方法(Hook Method)
-
工人的超類:Worker.java
// 具體方法 public final void workOneDay() { Log.e("workOneDay", "-----------------work start----------------"); enterCompany(); work(); exitCompany(); Log.e("workOneDay", "-----------------work end----------------"); } // 工作 抽象方法 public abstract void work(); // 鉤子方法 public boolean isNeedPrintDate() { return false; } private void exitCompany() { if (isNeedPrintDate()) { Log.e("exitCompany", "---" + new Date().toLocaleString() + "--->"); } Log.e("exitCompany", name + "---離開(kāi)公司"); }
-
程序員實(shí)現(xiàn)類(可得知時(shí)間):ITWorker.java
/** * 重寫(xiě)父類的此方法,使可以查看離開(kāi)公司時(shí)間 */ @Override public boolean isNeedPrintDate() { return true; }
-
最后測(cè)試:
-
查看所有人員的工作情況:
QAWorker qaWorker = new QAWorker("測(cè)試人員"); qaWorker(); HRWorker hrWorker = new HRWorker("莉莉姐"); hrWorker.workOneDay(); ...
-
查看程序猿離開(kāi)公司的時(shí)間:
ITWorker itWorker = new ITWorker("jingbin"); itWorker.workOneDay();
-
10. 狀態(tài)模式
定義:允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為,對(duì)象看起來(lái)好像修改了它的類。
定義又開(kāi)始模糊了,理一下,當(dāng)對(duì)象的內(nèi)部狀態(tài)改變時(shí),它的行為跟隨狀態(tài)的改變而改變了,看起來(lái)好像重新初始化了一個(gè)類似的。
需求:已自動(dòng)售貨機(jī)為例(有已投幣、未投幣等狀態(tài)和投幣、投幣等方法)
最初實(shí)現(xiàn)待改進(jìn)的售貨機(jī):VendingMachine.java
-
改進(jìn)后的售貨機(jī)(更具有延展性):VendingMachineBetter.java
// 放錢 public void insertMoney() { currentState.insertMoney(); } // 退錢 public void backMoney() { currentState.backMoney(); } // 轉(zhuǎn)動(dòng)曲柄 public void turnCrank() { currentState.turnCrank(); if (currentState == soldState || currentState == winnerState) { currentState.dispense();//兩種情況會(huì)出貨 } } // 出商品 public void dispense() { Log.e("VendingMachineBetter", "---發(fā)出一件商品"); if (count > 0) { count--; } } // 設(shè)置對(duì)應(yīng)狀態(tài) public void setState(State state) { this.currentState = state; }
狀態(tài)的接口:State.java
-
對(duì)應(yīng)狀態(tài)的接口實(shí)現(xiàn)類:
- eg: 中獎(jiǎng)狀態(tài):WinnerState.java
- eg: 售賣狀態(tài):SoldState.java
-
改進(jìn)后的售貨機(jī)測(cè)試:
// 初始化售貨機(jī),且里面有3個(gè)商品 VendingMachineBetter machineBetter = new VendingMachineBetter(3); machineBetter.insertMoney(); machineBetter.turnCrank();
參考鏈接
Thanks
End
感興趣的小伙伴可以Star哦~
GitHub:DesignPattern