爲何有模版模式:html
定義一個算法的骨架,而將一些步驟的實現延遲到子類中。模板方法使得子類能夠在不改變算法結構的狀況下,從新定義算法中某些步驟的具體實現;java
優勢: git
缺點:算法
在模版模式中,子類執行的結果影響了父類的結果,會增長代碼閱讀的難度;數據庫
應用場景:設計模式
實現:瀏覽器
模版模式中,基類方法的默認實現被退化爲鉤子Hooks的概念,他們被設計在子類中被重寫,若是你指望一些方法在子類中不被重寫,可讓他們爲final。例如,安全
模版抽象類微信
1 public abstract class HouseTemplate { 2 3 protected HouseTemplate(String name){ 4 this.name = name; 5 } 6 7 protected String name; 8 9 protected abstract void buildDoor(); 10 11 protected abstract void buildWindow(); 12 13 protected abstract void buildWall(); 14 15 protected abstract void buildBase(); 16 17 protected abstract void buildToilet(); 18 19 //鉤子方法 20 protected boolean isBuildToilet(){ 21 return true; 22 } 23 24 //公共邏輯 25 public final void buildHouse(){ 26 27 buildBase(); 28 buildWall(); 29 buildDoor(); 30 buildWindow(); 31 if(isBuildToilet()){ 32 buildToilet(); 33 } 34 } 35 36 }
子類1網絡
1 public class HouseOne extends HouseTemplate { 2 3 HouseOne(String name){ 4 super(name); 5 } 6 7 HouseOne(String name, boolean isBuildToilet){ 8 this(name); 9 this.isBuildToilet = isBuildToilet; 10 } 11 12 public boolean isBuildToilet; 13 14 @Override 15 protected void buildDoor() { 16 System.out.println(name +"的門要採用防盜門"); 17 } 18 19 @Override 20 protected void buildWindow() { 21 System.out.println(name + "的窗戶要面向北方"); 22 } 23 24 @Override 25 protected void buildWall() { 26 System.out.println(name + "的牆使用大理石建造"); 27 } 28 29 @Override 30 protected void buildBase() { 31 System.out.println(name + "的地基使用鋼鐵地基"); 32 } 33 34 @Override 35 protected void buildToilet() { 36 System.out.println(name + "的廁所建在東南角"); 37 } 38 39 @Override 40 protected boolean isBuildToilet(){ 41 return isBuildToilet; 42 } 43 44 }
子類2
1 public class HouseTwo extends HouseTemplate { 2 3 HouseTwo(String name){ 4 super(name); 5 } 6 7 @Override 8 protected void buildDoor() { 9 System.out.println(name + "的門採用木門"); 10 } 11 12 @Override 13 protected void buildWindow() { 14 System.out.println(name + "的窗戶要向南"); 15 } 16 17 @Override 18 protected void buildWall() { 19 System.out.println(name + "的牆使用玻璃製造"); 20 } 21 22 @Override 23 protected void buildBase() { 24 System.out.println(name + "的地基使用花崗岩"); 25 } 26 27 @Override 28 protected void buildToilet() { 29 System.out.println(name + "的廁所建在西北角"); 30 } 31 32 }
客戶端
1 public class Clienter { 2 3 public static void main(String[] args){ 4 HouseTemplate houseOne = new HouseOne("房子1", false); 5 HouseTemplate houseTwo = new HouseTwo("房子2"); 6 houseOne.buildHouse(); 7 houseTwo.buildHouse(); 8 } 9 10 }
能夠看到,經過重寫鉤子方法自定義了房子1不須要建造廁所(fasle),另外能夠參考這篇文章:設計模式學習筆記之九:模板方法模式。
爲何有策略模式:
Define a family of algorithms,encapsulate each one,and make them interchangeable.
定義一組算法(我以爲應該是一個行爲的不一樣實現吧),將每一個算法都封裝起來,而且使它們之間能夠互換。
通俗點講是利用繼承和多態機制,從而實現同一行爲在不一樣場景下的不一樣實現吧。
優勢:
缺點:
應用場景:
實現:
策略模式有三種角色:
抽象策略角色這個是一個抽象的角色,一般使用接口或者抽象類去實現。好比Comparator接口;
具體策略角色包裝了具體的算法和行爲。就是實現了Comparator接口的實現一組實現類;
環境角色內部會持有一個抽象角色的引用,好比內部必定會有一個策略類的一個成員變量,這樣在構建函數中就能夠接收外部傳遞的具體的策略類。
例如,
//抽象策略類 Strategy interface IStrategy { void algorithm(); } //具體策略類 ConcreteStrategyA static class ConcreteStrategyA implements IStrategy { @Override public void algorithm() { System.out.println("Strategy A"); } } //具體策略類 ConcreteStrategyB static class ConcreteStrategyB implements IStrategy { @Override public void algorithm() { System.out.println("Strategy B"); } }
客戶端,
1 //上下文環境角色 2 static class Context { 3 private IStrategy mStrategy; // 引用 4 5 public Context(IStrategy strategy) { 6 this.mStrategy = strategy; 7 } 8 9 public void algorithm() { 10 this.mStrategy.algorithm(); 11 } 12 }
爲何有命令模式:
將一個請求封裝成一個對象,從而使請求參數化。
優勢:
缺點:
應用場景:
在一些對行爲進行"記錄、撤銷/重作、事務"等處理的場合,好比:線程池、工做隊列、日誌請求等
在某一端添加命令,另外一端則是線程進行下面的動做:從隊列中取出一個命令,調用它的execute()方法,等待這個調用完成,而後將此命令對象丟棄,再取出下一個命令......這裏工做隊列和命令對象之間是徹底解耦的,線程可能此刻在進行財務運算,下一刻卻在讀取網絡數據。工做隊列對象不在意到底作些什麼,它們只知道取出命令對象,而後調用其execute()方法。相似地,它們只要實現命令模式的對象,就能夠放入隊列裏,當線程可用時,就調用此對象的execute()方法。
多用於數據庫管理系統的實現,咱們須要把一系列的操做記錄下來(如寫在硬盤上),在遇到系統故障時讀出來以恢復數據,如何實現?利用對象的序列化把對象保存起來(記錄日誌),在須要的時候反序列化(恢復事務)。
注:這裏的可撤銷的操做就是:放棄該操做,回到未執行該操做前的狀態。
有兩種基本的思路來實現可撤銷的操做:
① 一種是補償式,又稱反操做式
好比被撤銷的操做是加的功能, 那撤消的實現就變成減的功能;好比被撤銷的操做是打開的功能,那麼撤銷的實現就變成關閉的功能。
② 另一種方式是存儲恢復式
意思就是把操做前的狀態記錄下來,而後要撤銷操做的時候就直接恢復回去就能夠了。
實現:
該模式具備如下角色:
抽象命令接口Command:定義接口,聲明執行的方法。
具體的命令對象ConcreteCommand:持有接受者對象,完成具體的命令。
接受者對象Receiver:接受者對象,真正執行命令的對象。
傳遞命令對象Invoker:調用者,要求命令對象執行請求。
客戶端對象Client:建立具體命令對象而且設置接受者。
好比:顧客來到餐館點一碗麪(發出請求) -> 櫃檯服務員記錄下來(建立命令) -> 服務員把訂單扔給廚房 -> 廚師很快作好了一碗麪(請求被執行)。顧客不知道將由誰來作這碗麪,櫃檯服務員也不知道,廚師不知道是誰點了這碗麪,其中,訂單就起解耦的做用。
首先,寫命令接口
1 public interface Command { 2 public abstract void execute();//只須要定義一個統一的執行方法 3 }
而後是執行者
1 public abstract class Chef { 2 //在此定義廚師的公共屬性 3 4 //定義烹飪方法 5 public abstract void cook(); 6 }
具體執行者,作面的廚師和作餅的廚師
1 public class NoodlesChef extends Chef{ 2 3 @Override 4 public void cook() { 5 System.out.println("作好了一碗美味的拉麪"); 6 } 7 }
1 public class PieChef extends Chef{ 2 3 @Override 4 public void cook() { 5 System.out.println("作好了一塊香噴噴的大餅"); 6 } 7 }
各類具體命令
1 public class NoodlesCommand implements Command{ 2 private NoodlesChef chef;//專業作面的廚師 3 4 public NoodlesCommand(){ 5 chef = new NoodlesChef(); 6 } 7 8 @Override 9 public void execute() { 10 chef.cook(); 11 //調用其它須要的方法 12 } 13 }
1 public class PieCommand implements Command{ 2 private PieChef chef;//專業作餅的廚師 3 4 public PieCommand(){ 5 chef = new PieChef(); 6 } 7 8 @Override 9 public void execute() { 10 chef.cook(); 11 //調用其它須要的方法 12 } 13 }
調用者invoker
1 public class Waiter { 2 3 private Command command; 4 5 // 設置具體的命令 6 public void setCommand(Command command) { 7 this.command = command; 8 } 9 10 // 執行命令 11 public void doCommand() { 12 command.execute(); 13 } 14 }
客戶端,店館開張了
public class Test { public static void main(String[] args) { System.out.println("第一位客戶X先生"); System.out.println("X先生:你好,我須要一碗麪,我餓極了"); NoodlesCommand nCmd = new NoodlesCommand(); System.out.println("櫃檯服務員:好的廚房~~,接單"); Waiter.doCommand(); //安排廚師作 System.out.println("第二位客戶XX先生"); System.out.println("XX先生:你好,我須要一塊餅,20分鐘後來取"); PieCommand pCmd = new PieCommand(); System.out.println("櫃檯服務員:好的廚房~~,接單"); Waiter.doCommand(); // 安排廚師作 } }
爲何有責任鏈模式:
搞一條對象鏈,這樣請求不用知道具體執行的是哪個對象,就實現了請求與對象之間的解耦。
優勢:
缺點(針對不純責任鏈模式):
應用場景:
我以爲好多if/else,switch/case操做能夠考慮這個吧,仍是能充分利用這種模式的優勢唄,好比:
Netty 中的 Pipeline 和 ChannelHandler 經過責任鏈設計模式來組織代碼邏輯
Spring Security 使用責任鏈模式,能夠動態地添加或刪除責任(處理 request 請求)
Spring AOP 經過責任鏈模式來管理 Advisor
實現(具體參考:責任鏈模式妙用):
該模式具備如下角色:
抽象處理者(Handler):定義一個處理請求的接口(或抽象類),包含抽象處理方法和一個後繼鏈接。
具體處理者(Concrete Handler):實現抽象處理者的處理方法,判斷可否處理本次請求,若是能夠則處理,不然將該請求轉給它的後繼者。
客戶類(Client):建立處理鏈,並向鏈頭的具體處理者對象提交請求,它不關心處理細節和請求的傳遞過程。
抽象接口
1 public abstract class BaseCase { 2 // 爲 true 代表本身能夠處理該 case 3 private boolean isConsume; 4 public BaseCase(boolean isConsume) { 5 this.isConsume = isConsume; 6 } 7 // 下一個責任節點 8 private BaseCase nextCase; 9 public void setNextCase(BaseCase nextCase) { 10 this.nextCase = nextCase; 11 } 12 public void handleRequest() { 13 if (isConsume) { 14 // 若是當前節點能夠處理,直接處理 15 doSomething(); 16 } else { 17 // 若是當前節點不能處理,而且有下個節點,交由下個節點處理 18 if (null != nextCase) { 19 nextCase.handleRequest(); 20 } 21 } 22 } 23 abstract protected void doSomething(); 24 }
具體處理者,其中一個爲例
1 public class OneCase extends BaseCase { 2 public OneCase(boolean isConsume) { 3 super(isConsume); 4 } 5 6 @Override protected void doSomething() { 7 // TODO do something 8 System.out.println(getClass().getName()); 9 } 10 }
客戶端
1 String input = "1"; 2 OneCase oneCase = new OneCase("1".equals(input)); 3 TwoCase twoCase = new TwoCase("2".equals(input)); 4 DefaultCase defaultCase = new DefaultCase(true); 5 oneCase.setNextCase(twoCase); 6 twoCase.setNextCase(defaultCase); 7 oneCase.handleRequest();
爲何有狀態模式:
讓一個對象在其內部狀態改變的時候改變其行爲
優勢:
缺點:
應用場景:
行爲隨着狀態的改變而改變的時候,例如權限設計,人員的狀態不一樣即便執行相同的行爲結果也會不一樣,在這種狀況下須要考慮使用狀態模式。
實現:
該模式具備如下角色
環境(Context)角色:也稱爲上下文,它將與狀態相關的操做委託給當前狀態對象來處理;
抽象狀態(State)角色:接口,用來封裝對象中的特定狀態所對應的行爲;
具體狀態(Concrete State)角色:實現抽象狀態所對應的行爲。
好比:用戶登陸系統,同一個操做在不一樣狀態下會有不一樣的操做,先定義一個用戶狀態的接口
1 public interface LoginState { 2 public void loadUser(); 3 public void loginIn(); 4 public void loginOut(); 5 }
登陸成功狀態
1 public class LoginInState implements LoginState{ 2 3 @Override 4 public void loadUser() { 5 // TODO Auto-generated method stub 6 System.out.println("用戶數據載入成功"); 7 8 } 9 10 @Override 11 public void loginIn() { 12 // TODO Auto-generated method stub 13 System.out.println("已經登陸成功了"); 14 } 15 16 @Override 17 public void loginOut() { 18 // TODO Auto-generated method stub 19 System.out.println("已經登陸成功了,請按退出登陸按鈕"); 20 } 21 22 }
退出登陸狀態
1 public class LoginErrorState implements LoginState{ 2 @Override 3 public void loadUser() { 4 // TODO Auto-generated method stub 5 System.out.println("已經退出,請按登陸按鈕"); 6 } 7 8 @Override 9 public void loginIn() { 10 // TODO Auto-generated method stub 11 System.out.println("已經退出,請先登陸"); 12 } 13 14 @Override 15 public void loginOut() { 16 // TODO Auto-generated method stub 17 System.out.println("退出登陸成功"); 18 } 19 }
用LoginContext管理這兩個狀態
1 /** 2 * 登陸環境上下文 3 * @author ccj 4 * 5 */ 6 public class LoginContext { 7 private LoginState loginState=new LoginErrorState(); 8 9 /** 10 * 能夠根據需求,進行單例化,工廠化 11 */ 12 public void loginIn(){ 13 loginState=new LoginInState(); 14 loginState.loginIn(); 15 } 16 17 public void loginOut(){ 18 loginState=new LoginErrorState(); 19 loginState.loginOut(); 20 } 21 22 public void loadUser(){ 23 loginState.loadUser(); 24 } 25 }
客戶端按鍵
1 public class Test { 2 /** 3 * 將登陸登出兩個動做, 分別對應兩個按鈕 4 * @param args 5 */ 6 public static void main(String[] args) { 7 LoginContext context =new LoginContext(); 8 System.out.println("=======點擊登陸按鈕===>>=狀態:登陸=====>>選擇加載用戶數據==========="); 9 context.loginIn(); 10 context.loadUser(); 11 12 System.out.println("=====點擊退出按鈕>>======狀態:退出=======>>選擇加載用戶數據========="); 13 context.loginIn(); 14 context.loginOut(); 15 context.loadUser(); 16 } 17 }
測試結果
1 =======點擊登陸按鈕===>>=狀態:登陸=====>>選擇加載用戶數據=========== 2 已經登陸成功了 3 用戶數據載入成功 4 =====點擊退出按鈕>>======狀態:退出=======>>選擇加載用戶數據========= 5 已經登陸成功了 6 退出登陸成功 7 已經退出,請按登陸按鈕
⚠️注意:在行爲受狀態約束的時候使用狀態模式,並且狀態不超過5個。
策略模式和狀態模式對比:
策略模式須要客戶端必須徹底知曉全部的策略方法,纔可以知道究竟哪個策略是當前須要的。而狀態模式客戶端在使用的時候,能夠沒必要關心有哪些狀態,他只須要去調用環境的行爲就能夠了,在環境的內部維護了這些狀態,就是策略模式服務的對象是不固定的,可是狀態模式服務的對象是固定的,每次都是那一個。
爲何有觀察者模式:
主要是鬆解耦(對應Kafka依賴broker發佈訂閱的這種徹底的解耦方式),對於對象間一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴它的對象都會獲得通知並自動更新。
優勢:
下降了目標與觀察者之間的耦合關係;
缺點:
應用場景:
- 對象間存在一對多關係,一個對象的狀態改變會影響其餘對象,而不知道具體有多少對象須要被改變;
- 當一個抽象模型有兩個方面,其中一個方面依賴於另外一方面時,可將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用
實現:
該模式具備如下角色
抽象主題(Subject):提供一個用於保存觀察者對象的彙集類和增長、刪除觀察者對象的方法,以及通知全部觀察者的抽象方法;
具體主題(Concrete Subject):在具體主題內部狀態改變時,給全部登記過的觀察者發出通知。
抽象觀察者(Observer):爲全部的具體觀察者定義一個接口,在獲得主題更改通知時更新本身。
具體觀察者(Concrete Observer):實現抽象觀察者中定義的抽象方法,以便在獲得目標的更改通知時更新自身的狀態。
讓耦合的雙方依賴於抽象,而不是依賴於具體,從而使各自的變化不會影響另外一邊的變化,這是依賴倒轉原則的最佳體現。
首先是一個觀察者接口
1 public interface Subscriber { 2 int receive(String publisher, String articleName); 3 }
有一個具體的觀察者(微信客戶端),實現receive方法
1 public class WeChatClient implements Subscriber { 2 private String username; 3 4 public WeChatClient(String username) { 5 this.username = username; 6 } 7 8 @Override 9 public int receive(String publisher, String articleName) { 10 // 接收到推送時的操做 11 System.out.println(String.format("用戶<%s> 接收到 <%s>微信公衆號 的推送,文章標題爲 <%s>", username, publisher, articleName)); 12 return 0; 13 } 14 }
接着是一個抽象主題,該類維護了一個訂閱者列表,實現了訂閱、取消訂閱、通知全部訂閱者等功能
1 public class Publisher { 2 private List<Subscriber> subscribers; 3 private boolean pubStatus = false; 4 5 public Publisher() { 6 subscribers = new ArrayList<Subscriber>(); 7 } 8 9 protected void subscribe(Subscriber subscriber) { 10 this.subscribers.add(subscriber); 11 } 12 13 protected void unsubscribe(Subscriber subscriber) { 14 if (this.subscribers.contains(subscriber)) { 15 this.subscribers.remove(subscriber); 16 } 17 } 18 19 protected void notifySubscribers(String publisher, String articleName) { 20 if (this.pubStatus == false) { 21 return; 22 } 23 for (Subscriber subscriber : this.subscribers) { 24 subscriber.receive(publisher, articleName); 25 } 26 this.clearPubStatus(); 27 } 28 29 protected void setPubStatus() { 30 this.pubStatus = true; 31 } 32 33 protected void clearPubStatus() { 34 this.pubStatus = false; 35 } 36 }
最後是一個具體主題微信公衆號類,該類提供了 publishArticles
方法,用於發佈推送,當文章發佈完畢時調用父類的通知全部訂閱者方法
1 public class WeChatAccounts extends Publisher { 2 private String name; 3 4 public WeChatAccounts(String name) { 5 this.name = name; 6 } 7 8 public void publishArticles(String articleName, String content) { 9 System.out.println(String.format("\n<%s>微信公衆號 發佈了一篇推送,文章名稱爲 <%s>,內容爲 <%s> ", this.name, articleName, content)); 10 setPubStatus(); 11 notifySubscribers(this.name, articleName); 12 } 13 }
客戶端使用
1 public class Test { 2 public static void main(String[] args) { 3 WeChatAccounts accounts = new WeChatAccounts("小旋鋒"); 4 5 WeChatClient user1 = new WeChatClient("張三"); 6 WeChatClient user2 = new WeChatClient("李四"); 7 WeChatClient user3 = new WeChatClient("王五"); 8 9 accounts.subscribe(user1); 10 accounts.subscribe(user2); 11 accounts.subscribe(user3); 12 13 accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容..."); 14 15 accounts.unsubscribe(user1); 16 accounts.publishArticles("設計模式 | 單例模式及典型應用", "單例模式的內容...."); 17 } 18 }
輸出,當公衆號發佈一篇推送時,訂閱該公衆號的用戶可及時接收到推送的通知
1 <小旋鋒>微信公衆號 發佈了一篇推送,文章名稱爲 <設計模式 | 觀察者模式及典型應用>,內容爲 <觀察者模式的內容...> 2 用戶<張三> 接收到 <小旋鋒>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 3 用戶<李四> 接收到 <小旋鋒>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 4 用戶<王五> 接收到 <小旋鋒>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 5 6 <小旋鋒>微信公衆號 發佈了一篇推送,文章名稱爲 <設計模式 | 單例模式及典型應用>,內容爲 <單例模式的內容....> 7 用戶<李四> 接收到 <小旋鋒>微信公衆號 的推送,文章標題爲 <設計模式 | 單例模式及典型應用> 8 用戶<王五> 接收到 <小旋鋒>微信公衆號 的推送,文章標題爲 <設計模式 | 單例模式及典型應用>
爲何有中介者模式:
把原來對象之間多對多的交互變成一對多的交互;減小各個對象之間的耦合,能夠更方便的增長減小對象。
優勢:
簡化了對象之間的交互,將網狀結構轉換成相對簡單的星型結構,一對多關係更容易理解、維護和擴展;
缺點:
應用場景:
- 一組定義良好的對象,如今要進行復雜的相互通訊;
- 想經過一箇中間類來封裝多個類中的行爲,而又不想生成太多的子類。
示例不少,好比
MVC模式中,Controller 是中介者,根據View 層的請求來操做Model 層
- 好比組成原理中的主板,上邊插了CPU、內存、網卡、聲卡、硬盤各類東西
實現(參考https://blog.csdn.net/wwwdc1012/article/details/83389158):
該模式具備如下角色
抽象中介者(Mediator)角色:它是中介者的接口,提供了同事對象註冊與轉發同事對象信息的抽象方法。
具體中介者(ConcreteMediator)角色:實現中介者接口,定義一個 List 來管理同事對象,協調各個同事角色之間的交互關係,所以它依賴於同事角色。
抽象同事類(Colleague)角色:定義同事類的接口,保存中介者對象,提供同事對象交互的抽象方法,實現全部相互影響的同事類的公共功能。
具體同事類(Concrete Colleague)角色:實現了在抽象同事類中聲明的抽象方法,每一個同事都知道中介者對象,要與同事通訊則把通訊告訴中介者。
好比,具體同事類,兩個本身寫的定時任務
1 public class MyOneTask extends TimerTask { 2 private static int num = 0; 3 @Override 4 public void run() { 5 System.out.println("I'm MyOneTask " + ++num); 6 } 7 } 8 9 public class MyTwoTask extends TimerTask { 10 private static int num = 1000; 11 @Override 12 public void run() { 13 System.out.println("I'm MyTwoTask " + num--); 14 } 15 }
客戶端
1 public class TimerTest { 2 public static void main(String[] args) { 3 // 注意:多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常, 4 // 其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題 5 Timer timer = new Timer(); 6 timer.schedule(new MyOneTask(), 3000, 1000); // 3秒後開始運行,循環週期爲 1秒 7 timer.schedule(new MyTwoTask(), 3000, 1000); 8 } 9 }
輸出
1 I'm MyOneTask 1 2 I'm MyTwoTask 1000 3 I'm MyTwoTask 999 4 I'm MyOneTask 2 5 I'm MyOneTask 3 6 I'm MyTwoTask 998 7 I'm MyTwoTask 997 8 I'm MyOneTask 4 9 I'm MyOneTask 5 10 I'm MyTwoTask 996 11 I'm MyTwoTask 995 12 I'm MyOneTask 6 13 ...
具體中介者Timer
1 public class Timer { 2 private final TaskQueue queue = new TaskQueue(); 3 private final TimerThread thread = new TimerThread(queue); 4 public void schedule(TimerTask task, long delay) { 5 if (delay < 0) 6 throw new IllegalArgumentException("Negative delay."); 7 sched(task, System.currentTimeMillis()+delay, 0); 8 } 9 10 public void schedule(TimerTask task, Date time) { 11 sched(task, time.getTime(), 0); 12 } 13 14 private void sched(TimerTask task, long time, long period) { 15 if (time < 0) 16 throw new IllegalArgumentException("Illegal execution time."); 17 18 if (Math.abs(period) > (Long.MAX_VALUE >> 1)) 19 period >>= 1; 20 21 // 獲取任務隊列的鎖(同一個線程屢次獲取這個鎖並不會被阻塞,不一樣線程獲取時纔可能被阻塞) 22 synchronized(queue) { 23 // 若是定時調度線程已經終止了,則拋出異常結束 24 if (!thread.newTasksMayBeScheduled) 25 throw new IllegalStateException("Timer already cancelled."); 26 27 // 再獲取定時任務對象的鎖(爲何還要再加這個鎖呢?想不清) 28 synchronized(task.lock) { 29 // 判斷線程的狀態,防止多線程同時調度到一個任務時屢次被加入任務隊列 30 if (task.state != TimerTask.VIRGIN) 31 throw new IllegalStateException( 32 "Task already scheduled or cancelled"); 33 // 初始化定時任務的下次執行時間 34 task.nextExecutionTime = time; 35 // 重複執行的間隔時間 36 task.period = period; 37 // 將定時任務的狀態由TimerTask.VIRGIN(一個定時任務的初始化狀態)設置爲TimerTask.SCHEDULED 38 task.state = TimerTask.SCHEDULED; 39 } 40 41 // 將任務加入任務隊列 42 queue.add(task); 43 // 若是當前加入的任務是須要第一個被執行的(也就是他的下一次執行時間離如今最近) 44 // 則喚醒等待queue的線程(對應到上面提到的queue.wait()) 45 if (queue.getMin() == task) 46 queue.notify(); 47 } 48 } 49 50 // cancel會等到全部定時任務執行完後馬上終止定時線程 51 public void cancel() { 52 synchronized(queue) { 53 thread.newTasksMayBeScheduled = false; 54 queue.clear(); 55 queue.notify(); // In case queue was already empty. 56 } 57 } 58 // ... 59 }
Timer 中在 schedulexxx 方法中經過 TaskQueue 協調各類 TimerTask 定時任務,Timer 是中介者,TimerTask 是抽象同事類,而咱們本身寫的任務則是具體同事類
TimerThread 是 Timer 中定時調度線程類的定義,這個類會作爲一個線程一直運行來執行 Timer 中任務隊列中的任務。
Timer 這個中介者的功能就是定時調度咱們寫的各類任務,將任務添加到 TaskQueue 任務隊列中,給 TimerThread 執行,讓任務與執行線程解耦
注:
爲何有迭代器模式:
將遍歷和實現分開,順序的訪問集合內部的對象,而又不暴露集合內部的表示。
優勢:
缺點:
應用場景:
這個JDK已經提供了Iterator接口,咱們直接使用就行,如今不多本身寫迭代器了吧
實現
該模式具備如下角色:
抽象迭代器(Iterator):抽象迭代器負責定義訪問和遍歷元素的接口,並且基本上是有一些固定的方法:
- first()得到第一個元素
- next()訪問下一個元素
- hasNext()是否已經訪問到底部
具體迭代器(ConcreteIterator):具體迭代器角色要實現迭代器接口,完成容器元素的遍歷
抽象容器(Aggregate):容器角色負責提供建立具體迭代器角色的接口,必然提供一個相似createIterator()這樣的方法,在Java中通常是iterator()方法
具體容器(Concrete Aggregate):具體容器實現容器接口定義的方法,建立出容納迭代器的對象
好比,首先iterator,Iterator中最重要的兩個方法就是next方法和hasNext方法,構成了遍歷整個容器數據的兩個方法。remove方法若是沒有實現的話默認是會拋出一個不支持該操做的異常。
1 public interface Iterator<E> { 2 boolean hasNext(); 3 E next(); 4 default void remove() { 5 throw new UnsupportedOperationException(); 6 } 7 }
而後ConcreteIterator
1 private class Itr<E> implements Iterator<E> { 2 private int position = -1; 3 private Object[] data = elements; 4 @Override 5 public boolean hasNext() { 6 return ++ position < data.length; 7 } 8 9 @Override 10 public E next() { 11 return (E) data[position]; 12 } 13 }
而後是aggregate
1 public interface Iterable<E> { 2 Iterator<E> createIterator(); 3 }
concrete aggregate
1 public class MyContainer<E> implements Iterable<E>{ 2 3 Object[] elements; 4 public MyContainer() { 5 elements = new Byte[10]; 6 for (int i =0; i < 10; i ++) 7 elements[i] = (byte) i; 8 } 9 @Override 10 public Iterator<E> createIterator() { 11 return new Itr(); 12 } 13 14 }
客戶端
1 iterator it = new MyContainer<Byte>().createIterator(); 2 while (elements.hasNext()) { 3 System.out.println(elements.next()); 4 }
注:爲何不讓容器直接繼承Iterator接口,還要整個Iterable接口?
1 爲了提供容器內數據安全性。假設容器實現了Iterator接口,那麼咱們全部經過Iterator接口進行的數據訪問修改操做都會直接影響容器內的數據,由於咱們訪問的數據和容器維護的數據是同一份數據。因此不如讓Iterator接口訪問數據副原本的安全。本身用本身的,互不影響。
爲何有訪問者模式:
將穩定的數據結構和易變的數據訪問操做分離,知足開閉原則。
優勢:
缺點:
應用場景:
最複雜的設計模式,而且使用頻率不高。通常場景以下:
一、對象結構中對象對應的元素類不多改變,但常常須要在此對象結構上定義新的操做;
二、須要對一個對象結構中的元素進行不少不一樣的而且不相關的操做,而須要避免讓這些操做"污染"
實現:
抽象訪問者(Vistor):抽象訪問者爲對象結構中每個具體元素類聲明一個訪問操做,從這個操做的名稱或參數類型能夠清楚知道須要訪問的具體元素的類型,具體訪問者須要實現這些操做方法,定義對這些元素的訪問操做;
具體訪問者(ConcreteVisitor):具體訪問者實現了每一個由抽象訪問者聲明的操做;
抽象元素(Element):抽象元素通常是抽象類或者接口,它定義一個accept()方法,該方法一般以一個抽象訪問者做爲參數;
具體元素(ConcreteElement):具體元素實現了accept()方法,在accept()方法中調用訪問者的訪問方法以便完成對一個元素的操做;
對象結構(ObjectStructure):對象結構是一個元素的集合,它用於存放元素對象,而且提供了遍歷其內部元素的方法。
它實際上是對不一樣數據結構的訪問封裝,例如
1 // 抽象元素 2 public interface Element { 3 void accept(Visitor visitor); 4 } 5 // 具體元素A 6 public class ElementA implements Element { 7 @Override 8 public void accept(Visitor visitor) { 9 visitor.visit(this); 10 } 11 } 12 // 具體元素B 13 public class ElementB implements Element { 14 @Override 15 public void accept(Visitor visitor) { 16 visitor.visit(this); 17 } 18 } 19 // 抽象訪問者 20 public abstract class Visitor { 21 public abstract void visit(ElementA elementA); 22 public abstract void visit(ElementB elementB); 23 } 24 // 具體訪問者A 25 public class VisitorA extends Visitor { 26 @Override 27 public void visit(ElementA elementA) { 28 System.out.println(this + ":elementA"); 29 } 30 31 @Override 32 public void visit(ElementB elementB) { 33 System.out.println(this + ":elementB"); 34 } 35 } 36 // 對象結構 37 public class ObjectStructure { 38 private List<Element> elementList; 39 40 public ObjectStructure() { 41 this.elementList = new ArrayList<>(); 42 } 43 44 public void addElement(Element element) { 45 elementList.add(element); 46 } 47 48 public void removeElement(Element element) { 49 elementList.remove(element); 50 } 51 52 public void accept(Visitor visitor) { 53 elementList.forEach(element -> element.accept(visitor)); 54 } 55 } 56 // 客戶端 57 @Test 58 public void VisitorTest() { 59 ObjectStructure objectStructure = new ObjectStructure(); 60 objectStructure.addElement(new ElementA()); 61 objectStructure.addElement(new ElementB()); 62 objectStructure.accept(new VisitorA()); 63 }
輸出:
com.test.visitor.visitorA@33a10788:ElementA
com.test.visitor.visitorA@33a10788:ElementB
爲何有備忘錄模式:
保存對象的狀態,恢復對象的狀態
優勢:
缺點:
應用場景:
JDK、Spring不多用備忘錄的場景,通常經常使用於須要保存/恢復數據的相關狀態場景,好比:
瀏覽器回退:當咱們能夠在瀏覽器左上角點擊左箭頭回退到上一次的頁面,也能夠點擊右箭頭從新回到當前頁面
數據庫備份與還原:備份當前已有的數據或者記錄保留,還原已經保留的數據
編輯器撤銷與重作:寫錯時能夠按快捷鍵 Ctrl + z 撤銷,撤銷後能夠按 Ctrl + y 重作(md 居然如今才知道......)
虛擬機生成快照與恢復:虛擬機能夠生成一個快照,當虛擬機發生錯誤時能夠恢復到快照的樣子
Git版本管理:Git是最多見的版本管理軟件,每提交一個新版本,實際上Git就會把它們自動串成一條時間線,每一個版本都有一個版本號,使用 git reset --hard 版本號 便可回到指定的版本
棋牌遊戲悔棋:在棋牌遊戲中能夠悔棋,回退到上一步狀態
實現:
該模式具備如下角色:
發起人角色(Originator):當前時刻的內部狀態信息,提供建立備忘錄和恢復備忘錄數據的功能,實現其餘業務功能;
備忘錄角色(Memento):負責存儲發起人的內部狀態,在須要的時候提供這些內部狀態給發起人。
管理者角色(Caretaker):對備忘錄進行管理,提供保存與獲取備忘錄的功能,但其不能對備忘錄的內容進行訪問與修改。
還須要注意下寬接口和窄接口:
窄接口:只容許它把備忘錄對象傳給其餘的對象,針對的是負責人對象和其餘除發起人對象以外的任何對象。
寬接口: 容許它讀取全部的數據,以便根據這些數據恢復這個發起人對象的內部狀態,針對發起人。
1 // 備忘錄類 2 public class Memento { 3 private String state; 4 5 public Memento(String state){ 6 this.state = state; 7 } 8 9 public String getState(){ 10 return state; 11 } 12 }
1 // 發起人類 2 public class Originator { 3 private String state; 4 5 public void setState(String state){ 6 this.state = state; 7 } 8 9 public String getState(){ 10 return state; 11 } 12 13 public Memento saveStateToMemento(){ 14 return new Memento(state); 15 } 16 17 public void getStateFromMemento(Memento Memento){ 18 state = Memento.getState(); 19 } 20 }
1 // 管理者類 2 import java.util.ArrayList; 3 import java.util.List; 4 5 public class CareTaker { 6 private List<Memento> mementoList = new ArrayList<Memento>(); 7 8 public void add(Memento state){ 9 mementoList.add(state); 10 } 11 12 public Memento get(int index){ 13 return mementoList.get(index); 14 } 15 }
1 // 客戶端 2 public class MementoPatternDemo { 3 public static void main(String[] args) { 4 Originator originator = new Originator(); 5 CareTaker careTaker = new CareTaker(); 6 originator.setState("State #1"); 7 originator.setState("State #2"); 8 careTaker.add(originator.saveStateToMemento()); 9 originator.setState("State #3"); 10 careTaker.add(originator.saveStateToMemento()); 11 originator.setState("State #4"); 12 13 System.out.println("Current State: " + originator.getState()); 14 originator.getStateFromMemento(careTaker.get(0)); 15 System.out.println("First saved State: " + originator.getState()); 16 originator.getStateFromMemento(careTaker.get(1)); 17 System.out.println("Second saved State: " + originator.getState()); 18 } 19 }
// 輸出
Current State: State #4 First saved State: State #2 Second saved State: State #3
爲何有解釋器模式:
對於一些固定文法構建一個解釋句子的解釋器,其中文法是文法是用於描述語言語法結構的形式規則
優勢:
缺點:
應用場景:
解釋器模式在實際的軟件開發中使用比較少,由於它會引發效率、性能以及維護等問題。若是碰到對錶達式的解釋,在JAVA 中能夠用 Expression4J 或 Jep 等來設計。該模式主要應用在:
實現:
該模式具備如下角色:
抽象解釋器(AbstractExpression):它包含了解釋方法 interpret;
終結符表達式(TerminalExpression):實現與文法中的元素相關聯的解釋操做,一般一個解釋器模式中只有一個終結表達式,但對應不一樣的終結符,好比四則運算中的終結符就是運算元素;
非終結符表達式(NonterminalExpression):文法中的每條規則對應於一個非終結表達式,好比四則運算中的加法運算、減法運算等
上下文(Context): 上下文環境類,包含解釋器以外的全局信息,通常是 HashMap
實現一個簡單的計算器
1 // 解釋器接口 2 public interface Expression { 3 int interpreter(Context context);//必定會有解釋方法 4 } 5 6 // 終結符表達式(在這個例子,用來存放數字,或者表明數字的字符) 7 public class TerminalExpression implements Expression{ 8 9 String variable; 10 public TerminalExpression(String variable){ 11 12 this.variable = variable; 13 } 14 @Override 15 public int interpreter(Context context) { 16 return context.lookup(this); 17 } 18 } 19 20 // 抽象非終結符表達式 21 public abstract class NonTerminalExpression implements Expression{ 22 Expression e1,e2; 23 public NonTerminalExpression(Expression e1, Expression e2){ 24 25 this.e1 = e1; 26 this.e2 = e2; 27 } 28 } 29 30 // 加法運算 31 public class PlusOperation extends NonTerminalExpression { 32 33 public PlusOperation(Expression e1, Expression e2) { 34 super(e1, e2); 35 } 36 37 //將兩個表達式相加 38 @Override 39 public int interpreter(Context context) { 40 return this.e1.interpreter(context) + this.e2.interpreter(context); 41 } 42 } 43 44 // 減法表達式實現類 45 public class MinusOperation extends NonTerminalExpression { 46 47 public MinusOperation(Expression e1, Expression e2) { 48 super(e1, e2); 49 } 50 51 //將兩個表達式相減 52 @Override 53 public int interpreter(Context context) { 54 return this.e1.interpreter(context) - this.e2.interpreter(context); 55 } 56 } 57 58 59 // 上下文類(這裏主要用來將變量解析成數字【固然一開始要先定義】 60 public class Context { 61 private Map<Expression, Integer> map = new HashMap<>(); 62 63 //定義變量 64 public void add(Expression s, Integer value){ 65 map.put(s, value); 66 } 67 //將變量轉換成數字 68 public int lookup(Expression s){ 69 return map.get(s); 70 } 71 } 72 73 // 測試類 74 public class Test { 75 public static void main(String[] args) { 76 77 Context context = new Context(); 78 TerminalExpression a = new TerminalExpression("a"); 79 TerminalExpression b = new TerminalExpression("b"); 80 TerminalExpression c = new TerminalExpression("c"); 81 context.add(a, 4); 82 context.add(b, 8); 83 context.add(c, 2); 84 85 System.out.println(new MinusOperation(new PlusOperation(a,b), c).interpreter(context)); 86 } 87 } 88 89 運行結果以下 90 ----------------------------------- 91 10