標籤:java 設計模式 程式員 程式人生 互連網
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。
GitHub地址
DesignPattern
文章說明
一個Demo,集合常用的十種設計模式,每個模式使用易被人們接受的案例講述,按模式分包,使用設計模式前後對比,介面顯示定義講解,讓你更深刻的瞭解每種設計模式。
大部分案例來自張鴻洋的部落格。如有錯誤歡迎指正,如有侵權,請聯絡我刪除。
項目結構
包結構.png
設計模式分為三種類型,共23種:
建立型模式:單例模式、抽象原廠模式、建造者模式、原廠模式、原型模式。
結構型模式:適配器模式、橋接模式、裝飾模式、組合模式、面板模式、享元模式、代理模式。
行為型模式:模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解譯器模式、狀態模式、策略模式、職責鏈模式(責任鏈模式)、訪問者模式。
上面連結點擊直接跳轉到GitHub對應模式解析內
MainActivity.png
部落格目錄
1 . 設計模式 觀察者模式(Observer Pattern) 以公眾服務為例
2 . 設計模式 原廠模式(Factory Pattern) 從賣肉夾饃說起
3 . 設計模式 單例設計模式(Singleton Pattern) 完全解析
4 . 設計模式 策略模式(Strategy Pattern) 以角色遊戲為背景
5 . 設計模式 適配器模式(Adapter Pattern) 以手機充電器為例
6 . 設計模式 命令模式(Command Pattern) 管理智能家電
7 . 設計模式 裝飾者模式(Decorator Pattern) 帶你重回傳奇世界
8 . 設計模式 面板模式(Facade Pattern) 一鍵電影模式
9 . 設計模式 模版方法模式(TemplateMethod Pattern) 展現程式員的一天
10 . 設計模式 狀態模式(State Pattern) 以自動售貨機為例
對應模式所在的包
1 . Observer
2 . Factory
3 . Singleton
4 . Strategy
5 . Adapter
6 . Command
7 . Decorator
8 . Facade
9 . Template Method
10 . State
模式分析
- 觀察者模式
定義了對象之間的一對多的依賴,這樣一來,當一個對象改變時,它的所有的依賴者都會收到通知並自動更新。
對於JDK或者Andorid中都有很多地方實現了觀察者模式,比如XXXView.addXXXListenter , 當然了 XXXView.setOnXXXListener不一定是觀察者模式,因為觀察者模式是一種一對多的關係,對於setXXXListener是1對1的關係,應該叫回調。
專題介面:Subject.java ;
/**
- 註冊一個觀察者
*/
public void registerObserver(Observer observer);
/**
- 移除一個觀察者
*/
public void removeObserver(Observer observer);
/**
- 通知所有觀察者
*/
public void notifyObservers();
3D服務號的實作類別:ObjectFor3D.java
@Override
public void registerObserver(Observer observer) {br/>observers.add(observer);
}
@Override
int index = observers.indexOf(observer);
if (index >= 0) {br/>observers.remove(index);
}
}
@Override
for (Observer observer : observers) {
observer.update(msg);
}
}
/**
- 主題更新資訊
*/
public void setMsg(String msg) {
this.msg = msg;
notifyObservers();
}
所有觀察者需要實現此介面:Observer.java
public ObserverUser1(Subject subject) {br/>subject.registerObserver(this);
}
@Override
Log.e("-----ObserverUser1 ", "得到 3D 號碼:" + msg + ", 我要記下來。 ");
}
最後測試:ObserverActivity.java
// 建立服務號
objectFor3D = new ObjectFor3D();
// 建立兩個訂閱者
observerUser1 = new ObserverUser1(objectFor3D);
observerUser2 = new ObserverUser2(objectFor3D);
// 兩個觀察者,發送兩條資訊
objectFor3D.setMsg("201610121 的3D號為:127");
objectFor3D.setMsg("20161022 的3D號為:000");
- 原廠模式
簡單列一下這個模式的家族:
1、靜態原廠模式
這個最常見了,項目中的輔助類,TextUtil.isEmpty等,類+靜態方法。
2、簡單原廠模式(店裡買肉夾饃)
定義:通過專門定義一個類來負責建立其他類的執行個體,被建立的執行個體通常都具有共同的父類。
根據類型直接建立肉夾饃: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:// 預設為酸肉夾饃
roujiaMo = new ZSuanRoujiaMo();
break;
}
return roujiaMo;
}
3、Factory 方法模式(開分店)
定義:定義一個建立對象的介面,但由子類決定要執行個體化的類是哪一個。Factory 方法模式把類執行個體化的過程延遲到子類。
對比定義:
1、定義了建立對象的一個介面:public abstract RouJiaMo sellRoujiaMo(String type);
2、由子類決定執行個體化的類,可以看到我們的饃是子類產生的。
提供建立肉夾饃店抽象方法:RoujiaMoStore.java
public abstract RoujiaMo sellRoujiaMo(String type);
具體實現抽象方法:XianRoujiaMoStore.java
分店依舊使用簡單原廠模式:XianSimpleRoujiaMoFactory.java
4、抽象原廠模式(使用官方提供的原料)
定義:提供一個介面,用於建立相關的或依賴對象的家族,而不需要明確指定具體類。
對比定義:
1、提供一個介面:public interface RouJiaMoYLFactroy
2、用於建立相關的或依賴對象的家族 public Meat createMeat();public YuanLiao createYuanliao();我們介面用於建立一系列的原材料。
建立用於提供原料的介面工廠:RoujiaMoYLFactory.java
各自分店實現介面,完成原料提供:XianRoujiaMoYLFoctory.java
準備時,使用官方的原料:RoujiaMo.java
/**
- 準備工作
*/
public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
Meet meet = roujiaMoYLFactory.creatMeet();
YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成準備工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
}
- 單例設計模式
單例模式主要是為了避免因為建立了多個執行個體造成資源的浪費,且多個執行個體由於多次調用容易導致結果出現錯誤,而使用單例模式能夠保證整個應用中有且只有一個執行個體。
定義:只需要三步就可以保證對象的唯一性
(1) 不允許其他程式用new對象
(2) 在該類中建立對象
(3) 對外提供一個可以讓其他程式擷取該對象的方法
對比定義:
(1) 私人化該類的建構函式
(2) 通過new在本類中建立一個本類對象
(3) 定義一個公有的方法,將在該類中所建立的對象返回
餓漢式[可用]:SingletonEHan.java
含懶漢式[雙重校正鎖 推薦用]: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;
}
內部類[推薦用]:SingletonIn.java
枚舉[推薦用]:SingletonEnum.java
單例設計模式.png
- 策略模式
策略模式:定義了演算法族,分別封裝起來,讓它們之間可相互替換,此模式讓演算法的變化獨立於使用演算法的客戶。
以建立遊戲角色為例子:
最初的遊戲角色的父類:Role.java
發現有重複代碼後,重構後的父類:Role.java
總結:
1、封裝變化(把可能變化的代碼封裝起來)
2、多用組合,少用繼承(我們使用組合的方式,為客戶設定了演算法)
3、針對介面編程,不針對實現(對於Role類的設計完全的針對角色,和技能的實現沒有關係)
最後測試:建立角色:
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();// 防禦
- 適配器模式
定義:將一個類的介面轉換成客戶期望的另一個介面,適配器讓原本介面不相容的類可以相互合作。這個定義還好,說適配器的功能就是把一個介面轉成另一個介面。
以充電器為執行個體: 手機充電器一般都是5V左右吧,咱天朝的家用交流電壓220V,所以手機充電需要一個適配器(降壓器)
一部手機: Mobile.java
手機依賴一個提供5V電壓的介面: V5Power.java
我們擁有的是220V家用交流電: V220Power.java
適配器,完成220V轉5V的作用:V5PowerAdapter.java
最後測試:給手機沖個電:
Mobile mobile = new Mobile();
V5Power v5Power = new V5PowerAdapter(new V200Power());
mobile.inputPower(v5Power);
- 命令模式
定義:將“請求”封裝成對象,以便使用不同的請求、隊列或者日誌來參數化其他對象。命令模式也支援可撤銷的操作。(簡化: 將請求封裝成對象,將動作要求者和動作執行者解耦。)
需求:最近智能家電很火熱,假設現在有電視、電腦、電燈等家電,現在需要你做個遙控器控制所有家電的開關,要求做到每個按鈕對應的功能供使用者個人化,對於新買入家電要有非常強的擴充性。
1、家電的API:Door.java
2、把命令封裝成類:
統一的命令介面:Command.java
家電實現該介面:DoorOpenCommand.java
3、遙控器:ControlPanel.java
4、定義一個命令,可以幹一系列的事情: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、遙控器面板執行:CommandActivity.java
controlPanel.setCommands(0, new DoorOpenCommand(door));// 開門
controlPanel.keyPressed(0);
命令模式
- 裝飾者模式
裝飾者模式:若要擴充功能,裝飾者提供了比整合更有彈性的替代方案,動態地將責任附加到對象上。
先簡單描述下裝飾者模式發揮作用的地方,當我們設計好了一個類,我們需要給這個類添加一些輔助的功能,並且不希望改變這個類的代碼,這時候就是裝飾者模式大展雄威的時候了。這裡還體現了一個原則:類應該對擴充開放,對修改關閉。
需求:設計遊戲的裝備系統,基本要求,要可以計算出每種裝備在鑲嵌了各種寶石後的×××力和描述:
1、裝備的超類:IEquip.java
2、各個裝備的實作類別:
eg:武器的實作類別: ArmEquip.java
3、裝飾品的超類(裝飾品也屬於裝備):IEquipDecorator.java
4、裝飾品的實作類別:
eg:藍寶石的實作類別(可累加): BlueGemDecorator.java
5、最後測試:計算×××力和查看描述:
Log.e("---", "一個鑲嵌2顆紅寶石,1顆藍寶石的靴子: ");
IEquip iEquip = new RedGemDecotator(new RedGemDecotator(new BlueGemDecotator(new ShoeEquip())));
Log.e("---", "×××力:" + iEquip.caculateAttack());
Log.e("---", "描述語:" + iEquip.description());
- 面板模式
定義:提供一個統一的介面,用來訪問子系統中的一群介面,外觀定義了一個高層的介面,讓子系統更容易使用。其實就是為了方便客戶的使用,把一群操作,封裝成一個方法。
需求:我比較喜歡看電影,於是買了投影儀、電腦、音響、設計了房間的燈光、買了爆米花機,然後我想看電影的時候,我需要一鍵觀影和一鍵關閉。
每個裝置類的開關等操作:
eg: 爆米花機:PopcornPopper.java
電影院類:HomeTheaterFacade.java
/**
- 一鍵觀影
*/
public void watchMovie() {
computer.on();
light.down();
popcornPopper.on();
popcornPopper.makePopcorn();
projector.on();
projector.open();
player.on();
player.make3DListener();
}
最後測試:一鍵觀影:
new HomeTheaterFacade(computer, light, player, popcornPopper, projector).watchMovie();
- 模板方法模式
定義:定義了一個演算法的骨架,而將一些步驟延遲到子類中,模版方法使得子類可以在不改變演算法結構的情況下,重新定義演算法的步驟。
需求:簡單描述一下:本公司有程式猿、測試、HR、專案經理等人,下面使用模版方法模式,記錄下所有人員的上班情況
模板方法模式中的三類角色
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 + "---離開公司");
}
程式員實作類別(可得知時間):ITWorker.java
/**
- 重寫父類的此方法,使可以查看離開公司時間br/>*/
@Override
return true;
}
最後測試:
查看所有人員的工作情況:
QAWorker qaWorker = new QAWorker("測試人員");
qaWorker();
HRWorker hrWorker = new HRWorker("莉莉姐");
hrWorker.workOneDay();
...
查看程式猿離開公司的時間:
ITWorker itWorker = new ITWorker("jingbin");
itWorker.workOneDay();
- 狀態模式
定義:允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類。
定義又開始模糊了,理一下,當對象的內部狀態改變時,它的行為跟隨狀態的改變而改變了,看起來好像重新初始化了一個類似的。
需求:已自動售貨機為例(有已投幣、未投幣等狀態和投幣、投幣等方法)
最初實現待改進的售貨機:VendingMachine.java
改進後的售貨機(更具有延展性):VendingMachineBetter.java
// 放錢
public void insertMoney() {
currentState.insertMoney();
}
// 退錢
public void backMoney() {
currentState.backMoney();
}
// 轉動曲柄
public void turnCrank() {
currentState.turnCrank();
if (currentState == soldState || currentState == winnerState) {
currentState.dispense();//兩種情況會出貨
}
}
// 出商品
public void dispense() {
Log.e("VendingMachineBetter", "---發出一件商品");
if (count > 0) {
count--;
}
}
// 設定對應狀態
public void setState(State state) {
this.currentState = state;
}
狀態的介面:State.java
對應狀態的介面實作類別:
eg: 中獎狀態:WinnerState.java
eg: 售賣狀態:SoldState.java
改進後的售貨機測試:
// 初始化售貨機,且裡面有3個商品
VendingMachineBetter machineBetter = new VendingMachineBetter(3);
machineBetter.insertMoney();
machineBetter.turnCrank();
參考連結
http://blog.csdn.net/lmj623565791/article/category/2206597
Thanks
java 常用十種設計模式樣本歸納 | 已打包請帶走