常用設計模式的實現,以及Netty中的設計模式
1.觀察者模式
有兩個角色,觀察者和被觀察者。當被觀察者發出訊息後,註冊了的觀察者會收到其訊息,而沒有註冊的觀察者就不會收到。
//定義觀察者介面 interface Observer{ //通知觀察者 void notify(String message); } //定義被觀察者 interface Observed{ //註冊觀察者 void registerObserver(Observer o); //移除觀察者 void removeObserver(Observer o); //通知觀察者 void notifyObserver(); } //實現一個被觀察者(女神) class Gril implements Observed{ //女神最近的訊息 private String message; //追求女神的人 List<Observer> observerList; public Gril(){ observerList=new ArrayList<>(); } @Override public void registerObserver(Observer o) { //多了一位追求者 observerList.add(o); } @Override public void removeObserver(Observer o) { //一位勇士放棄了 observerList.remove(o); } @Override public void notifyObserver() { //女神發出了一點訊息 for(Observer o:observerList){ o.notify(message); } } public void setMeesage(String message){ this.message=message; } } //實現觀察者 class Handsome_Boy implements Observer{ @Override public void notify(String message) { System.out.println("隔壁班帥哥收到訊息:"+message); } } class Lao_Wang implements Observer{ @Override public void notify(String message) { System.out.println("老王收到訊息:"+message); } } class Gay implements Observer{ @Override public void notify(String message) { System.out.println("小夥汁收到訊息:"+message); } } //測試使用 public class observer_test { public static void main(String[] args) { //首先建立幾個觀察者和被觀察者 Gril gril=new Gril(); Handsome_Boy boy=new Handsome_Boy(); Gay gay=new Gay(); Lao_Wang wang=new Lao_Wang(); //註冊觀察者 gril.registerObserver(boy); gril.registerObserver(wang); //被觀察者發出通知 gril.setMeesage("我好無聊啊"); gril.notifyObserver(); } }
Netty中的應用:NioSocketChannel.writeAndFlush()。
2.責任鏈模式
責任鏈模式,讓多個物件都有可能處理同一個請求,把多個物件連成一條鏈,讓事件在這條鏈上傳播,並且鏈上每個節點都可以終止傳播 。熟悉Netty的朋友一定了解過這種設計模式,pipeline就像一個責任鏈,ChannelHandler就是其中處理邏輯的節點。
//建立一個邏輯處理器的抽象類 abstract class AbstractHandler { //下一個邏輯處理器,如果你想雙向傳播,還可以定義一個前節點 AbstractHandler nextHandler; //執行事件並往下傳播 public void Execute(String message) { write(message); //可以加上其他條件,終止傳播 if (nextHandler != null) nextHandler.Execute(message); } //設定下一個邏輯處理器 public void setNextHandler(AbstractHandler handler) { this.nextHandler = handler; } //實際的邏輯方法,需要具體的邏輯處理器去實現 abstract void write(String message); } //邏輯處理器A class HandlerA extends AbstractHandler { //實際的邏輯程式碼 @Override void write(String message) { System.out.println("邏輯處理器A執行:" + message); } } //邏輯處理器B class HandlerB extends AbstractHandler { @Override void write(String message) { System.out.println("邏輯處理器B執行:" + message); } } //邏輯處理器C class HandlerC extends AbstractHandler { @Override void write(String message) { System.out.println("邏輯處理器C執行:" + message); } } //測試使用 public class Chain_ResponsibilityTest { public static void main(String[] args) { //首先建立邏輯處理器物件 AbstractHandler a = new HandlerA(); AbstractHandler b = new HandlerB(); AbstractHandler c = new HandlerC(); //把多個物件連成一條鏈 a.setNextHandler(b); b.setNextHandler(c); //從頭節點開始執行 a.Execute("你好"); } }
最後ABC會按照連結串列順序輸出你好。
3.單例模式
單例模式的特點是一個類全域性只有一個變數,建立時是執行緒安全的。
常見單例模式實現的程式碼:
public class Singleton { private static Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if(singleton==null){ synchronized (Singleton.class){ if(singleton==null) singleton=new Singleton(); } } return singleton; } }
重點在於私有化建構函式,然後定義一個私有的靜態全域性變數,用以儲存當前類的例項。向外提供一個獲取例項的方法,如果當前例項變數不為空,說明已經例項化過,就直接返回。否則就進行例項化,為了防止多個執行緒同時進入if裡面重複例項化,所以得加上synchronized。
另外,單例模式還有懶漢、餓漢之分。上面的程式碼就是一個懶漢單例模式,即獲取例項時才去建立,這和Netty中的延遲載入是一個思想。而餓漢就是,定義例項變數時直接例項化了,同樣要私有化建構函式,之後獲取例項的方法直接返回這個變數就行。
單例模式在Netty中的應用:ReadTimeoutException等。
4.策略模式
簡答地說,一個類的行為或演算法可以在執行時更改,這就策略模式。當一個類需要根據執行時的資料,自動去選擇執行什麼邏輯,這時候我們就可以用上策略模式。下面來簡單實現一下:
//定義一個行為介面 interface Calculate{ int operation(int num1,int num2); } //繼承行為介面,實現具體的行為或演算法 class StrategyAdd implements Calculate{ @Override public int operation(int num1,int num2) { return num1+num2; } } class StrategyMultiply implements Calculate{ @Override public int operation(int num1, int num2) { return num1*num2; } } //封裝一個供外部使用的類 class Use{ private Calculate calculate; //接收的引數是那個父介面,這樣實現了這個介面的類都可以傳遞進來 public Use(Calculate calculate){ this.calculate=calculate; } public int execute(int num1,int num2){ //執行實際傳遞進來的類的operation方法 return calculate.operation(num1,num2); } } public class Strategy { //測試使用 public static void main(String[] args) { //假設這不是main方法,只是實際專案中的一個普通方法,args是使用者或其他路徑傳入的一個引數 //根據自己的實際需求甄別引數,選擇具體行為 if(args.length==0){ Use use=new Use(new StrategyAdd()); System.out.println(use.execute(5,5));//10 }else { Use use1=new Use(new StrategyMultiply()); System.out.println(use1.execute(5,5));//25 } } }
Netty中的應用:DefaultEventExecutorChooserFactor-newChooser
5.裝飾者模式
不需要修改原有類的程式碼,就可以對這個類的物件附加其他效果。當要拓展一個類的功能時就可以使用這種設計模式。但這種設計模式的缺點也非常明顯,會有額外的程式碼,當繼承的層級多了以後,邏輯也更復雜。
//定義一個基礎介面,獲取商品價格 interface Goods{ float getPrice(); } //實現一個汽車商品,獲取價格(原型) class Car implements Goods{ private float Price; public Car(float price){ this.Price=price; } @Override public float getPrice() { return Price; } } //裝飾者的父類,集中它們的優惠效果。如何集中的,關鍵在於裝飾類獲取價格方法時呼叫了父類的get方法。 //可以除錯捋一捋,最終的價格計算其實是在打折時才開始一層層往上計算的 class On_Sale implements Goods{ private Goods goods; public On_Sale(Goods goods){ this.goods=goods; } @Override public float getPrice() { return this.goods.getPrice(); } } //汽車立減優惠(裝飾者類,原型附加功能) class Car_Knock extends On_Sale{ //立減金額 private float amount; public Car_Knock(float amount,Goods goods){ super(goods); this.amount=amount; } @Override public float getPrice() { return super.getPrice()-amount; } } //汽車打折優惠 class Car_Discount extends On_Sale{ //打折多少 private int discount; public Car_Discount(int discount,Goods goods){ super(goods); this.discount=discount; } @Override public float getPrice() { return super.getPrice()*discount/10; } } //測試使用 public class decorator { public static void main(String[] args) { //建立一個原型,並傳入價格 Goods goods=new Car(120000); //當立減1000後 goods=new Car_Knock(1000,goods); //在立減基礎上再打個8折 goods=new Car_Discount(8,goods); System.out.println(goods.getPrice()); } }
Netty中的應用:WrappedByteBuf、UnreleasableByteBuf、SimpleLeakAwareByteBuf。第一個類就相當於裝飾者父類,後兩個就是裝飾類,而ByteBuf就是原